See the Scapy project page for more details on Scapy, what makes it different from most other networking tools, how to install it or some examples.
A test campaign is compounded of one or more test sets. A test set is a set of unit tests. An unit test is a list of Scapy commands that will be run by Scapy and whose end result will determine the truth value of the test.
Each test set and each unit test can be given some keywords. When running the campaign, tests can be selected according to their keyworkds.
For each unit test, test set and campaign, a CRC32 of the test is calculated and displayed so that an abstract of the test is sufficient to check the actual test was the one you expected and not one that has been slightliy modified for any reason. In case your dealing with evil people that try to modify or corrupt the file without changing the CRC32, a global SHA1 is computed on the whole file.
If you are interested in those kind of tests, you should also have a look at Scapytain, web application that can store, organise and run test campaigns on top of Scapy
Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-d|-D] [-F] [-q[q]] -l : generate local files -F : expand only failed tests -d : dump campaign -D : dump campaign and stop -C : don't calculate CRC and SHA -q : quiet mode -qq : [silent mode] -n <testnum> : only tests whose numbers are given (eg. 1,3-7,12) -m <module> : additional module to put in the namespace -k <kw1>,<kw2>,... : include only tests with one of those keywords (can be used many times) -K <kw1>,<kw2>,... : remove tests with one of those keywords (can be used many times)
# ./UTscapy.py -t demo_campaign.txt -f html -o demo_campaign.html -F passed 14F389A8 Get conf passed 19EC7768 List layers passed B614219C List commands passed 17F96DD3 Configuration failed 80FE5B0D Fake wrong test passed 2306670D Building some packets packet passed 1E674999 Manipulating some packets passed A572EE61 Checking overloads passed 63973A6A sprintf() function passed DCD84ABB sprintf() function passed 111C8A3A haslayer function passed 47614F5A getlayer function passed 6D1CC6B7 equality passed 5D24A719 Creation of a layer with FieldLenField passed 201D5022 Assembly of an empty packet passed 174DF639 Assembly of non empty packet passed 5CB93CC7 Disassembly passed F2BF1D32 Creation of a layer passed EF5DAC0E Assembly of an empty packet passed 92FBE492 Assembly of a non-empty packet passed 60BD0B6E Disassemble passed 91BCBCC8 Manipulate passed D82F28CC Create a layer passed 33099673 Test the PacketListField assembly passed A39DF413 Test the PacketListField assembly 2 passed 7ACA7707 Test disassembly passed E2DAAA2E Nested PacketListField passed 545C3B6B ISAKMP creation passed EFF2F68C ISAKMP manipulation passed 98051DEF ISAKMP assembly passed A0CD4650 ISAKMP disassembly passed 19D713FC WEP tests passed 0CF1FB69 Sending and receiving an ICMP passed B6AA1633 DNS request passed 68FE0D72 Implicit logic passed 87D8199C Port scan passed 97755875 Traceroute function passed 4A3C3B08 Result manipulation passed 658B8F34 DNS packet manipulation passed 70C79069 Arping Campaign CRC=7174A369 SHA=BA9669C94F64634488397E1558D8D86EDED7CF03 PASSED=39 FAILED=1You can see the result of this test campaign.
% Regression tests for Scapy # More informations at http://www.secdev.org/projects/UTscapy/ # $Id: regression.uts,v 1.4 2006/07/18 18:17:21 pbi Exp pbi $ ############ ############ + Informations on Scapy = Get conf ~ conf command * Dump the current configuration conf = List layers ~ conf command ls() = List commands ~ conf command lsc() = Configuration ~ conf conf.debug_dissect=1 ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken * and to catch Exceptions = Fake wrong test a = 3 assert(a == 3) a+1 == 3 = Building some packets packet ~ basic IP TCP UDP NTP LLC SNAP Dot11 IP()/TCP() Ether()/IP()/UDP()/NTP() Dot11()/LLC()/SNAP()/IP()/TCP()/"XXX" IP(ttl=25)/TCP(sport=12, dport=42) = Manipulating some packets ~ basic IP TCP a=IP(ttl=4)/TCP() a.ttl a.ttl=10 del(a.ttl) a.ttl TCP in a a[TCP] a[TCP].dport=[80,443] a a=3 = Checking overloads ~ basic IP TCP Ether a=Ether()/IP()/TCP() a.proto _ == 6 = sprintf() function ~ basic sprintf Ether IP UDP NTP a=Ether()/IP()/IP(ttl=4)/UDP()/NTP() a.sprintf("%type% %IP.ttl% %#05xr,UDP.sport% %IP:2.ttl%") _ in [ '0x800 64 0x07b 4', 'IPv4 64 0x07b 4'] = sprintf() function ~ basic sprintf IP TCP SNAP LLC Dot11 * This test is on the conditionnal substring feature of sprintf() a=Dot11()/LLC()/SNAP()/IP()/TCP() a.sprintf("{IP:{TCP:flags=%TCP.flags%}{UDP:port=%UDP.ports%} %IP.src%}") _ == 'flags=S 127.0.0.1' = haslayer function ~ basic haslayer IP TCP ICMP ISAKMP x=IP(id=1)/ISAKMP_payload_SA(prop=ISAKMP_payload_SA(prop=IP()/ICMP()))/TCP() TCP in x, ICMP in x, IP in x, UDP in x _ == (True,True,True,False) = getlayer function ~ basic getlayer IP ISAKMP UDP x=IP(id=1)/ISAKMP_payload_SA(prop=IP(id=2)/UDP(dport=1))/IP(id=3)/UDP(dport=2) x[IP] x[IP:2] x[IP:3] x[IP:4] x[UDP] x[UDP:1] x[UDP:2] x[IP].id == 1 and x[IP:2].id == 2 and x[IP:3].id == 3 and \ x[UDP].dport == 1 and x[UDP:2].dport == 2 and x[UDP:3] is None = equality ~ basic w=Ether()/IP()/UDP(dport=53) x=Ether()/IP(dst="127.0.0.1")/UDP() y=Ether()/IP()/UDP(dport=4) z=Ether()/IP()/UDP()/NTP() t=Ether()/IP()/TCP() x==y, x==z, x==t, y==z, y==t, z==t, w==x _ == (False, False, False, False, False, False, True) ############ ############ + Tests on FieldLenField = Creation of a layer with FieldLenField ~ field class TestFLenF(Packet): name = "test" fields_desc = [ FieldLenField("len", None, "str", "B"), StrLenField("str", "default", "len", shift=1) ] = Assembly of an empty packet ~ field TestFLenF() str(_) _ == "\x08default" = Assembly of non empty packet ~ field TestFLenF(str="123") str(_) _ == "\x04123" = Disassembly ~ field TestFLenF("\x04ABCDEFGHIJKL") _ _.len == 4 and _.str == "ABC" and Raw in _ ############ ############ + Tests on FieldListField = Creation of a layer ~ field class TestFLF(Packet): name="test" fields_desc = [ FieldLenField("len", None, "lst", "B"), FieldListField("lst", None, IntField("elt",0), "len") ] = Assembly of an empty packet ~ field a = TestFLF() str(a) = Assembly of a non-empty packet ~ field a = TestFLF() a.lst = [7,65539] ls(a) str(a) _ == struct.pack("!BII", 2,7,65539) = Disassemble ~ field TestFLF("\x00\x11\x12") assert(_.len == 0 and Raw in _ and _[Raw].load == "\x11\x12") TestFLF(struct.pack("!BIII",3,1234,2345,12345678)) assert(_.len == 3 and _.lst == [1234,2345,12345678]) = Manipulate ~ field a = TestFLF(lst=[4]) str(a) assert(_ == "\x01\x00\x00\x00\x04") a.lst.append(1234) TestFLF(str(a)) a.show2() a.len=7 str(a) assert(_ == "\x07\x00\x00\x00\x04\x00\x00\x04\xd2") a.len=2 a.lst=[1,2,3,4,5] TestFLF(str(a)) assert(Raw in _ and _[Raw].load == '\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05') ############ ############ + PacketListField tests = Create a layer ~ field class TestPLF(Packet): name="test" fields_desc=[ FieldLenField("len", None, "plist"), PacketListField("plist", [], IP, "len",) ] = Test the PacketListField assembly ~ field x=TestPLF() str(x) _ == "\x00\x00" = Test the PacketListField assembly 2 ~ field x=TestPLF() x.plist=[IP()/TCP(), IP()/UDP()] str(x) _.startswith('\x00\x02E') = Test disassembly ~ field x=TestPLF(plist=[IP()/TCP(seq=1234567), IP()/UDP()]) TestPLF(str(x)) _.show() IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 1234567 = Nested PacketListField ~ field y=IP()/TCP(seq=111111)/TestPLF(plist=[IP()/TCP(seq=222222),IP()/UDP()]) TestPLF(plist=[y,IP()/TCP(seq=333333)]) _.show() IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 111111 and _[TCP:2].seq==222222 and _[TCP:3].seq == 333333 ############ ############ + ISAKMP transforms test = ISAKMP creation ~ IP UDP ISAKMP p=IP(src='192.168.8.14',dst='10.0.0.1')/UDP()/ISAKMP()/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal(trans=ISAKMP_payload_Transform(transforms=[('Encryption', 'AES-CBC'), ('Hash', 'MD5'), ('Authentication', 'PSK'), ('GroupDesc', '1536MODPgr'), ('KeyLength', 256), ('LifeType', 'Seconds'), ('LifeDuration', 86400L)])/ISAKMP_payload_Transform(res2=12345,transforms=[('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'PSK'), ('GroupDesc', '1024MODPgr'), ('LifeType', 'Seconds'), ('LifeDuration', 86400L)]))) p.show() p = ISAKMP manipulation ~ ISAKMP p[ISAKMP_payload_Transform:2] _.res2 == 12345 = ISAKMP assembly ~ ISAKMP hexdump(p) str(p) == "E\x00\x00\x96\x00\x01\x00\x00@\x11\xa7\x9f\xc0\xa8\x08\x0e\n\x00\x00\x01\x01\xf4\x01\xf4\x00\x82\xbf\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x00\x00\x00^\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00R\x01\x01\x00\x00\x03\x00\x00'\x00\x01\x00\x00\x80\x01\x00\x07\x80\x02\x00\x01\x80\x03\x00\x01\x80\x04\x00\x05\x80\x0e\x01\x00\x80\x0b\x00\x01\x00\x0c\x00\x03\x01Q\x80\x00\x00\x00#\x00\x0109\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x01\x80\x04\x00\x02\x80\x0b\x00\x01\x00\x0c\x00\x03\x01Q\x80" = ISAKMP disassembly ~ ISAKMP q=IP(str(p)) q.show() q[ISAKMP_payload_Transform:2] _.res2 == 12345 ############ ############ + Dot11 tests = WEP tests ~ wifi wep Dot11 LLC SNAP IP TCP conf.wepkey = "ABCDEFGH" str(Dot11WEP()/LLC()/SNAP()/IP()/TCP(seq=12345678)) assert(_ == '\x00\x00\x00\x00\x1e\xafK5G\x94\xd4m\x81\xdav\xd4,c\xf1\xfe{\xfc\xba\xd6;T\x93\xd0\t\xdb\xfc\xa5\xb9\x85\xce\x05b\x1cC\x10\xd7p\xde22&\xf0\xbcUS\x99\x83Z\\D\xa6') Dot11WEP(_) assert(TCP in _ and _[TCP].seq == 12345678) ############ ############ + Network tests * Those tests need network access = Sending and receiving an ICMP ~ netaccess IP ICMP x=sr1(IP(dst="www.apple.com")/ICMP(),timeout=3) x x is not None and ICMP in x and x[ICMP].type == 0 = DNS request ~ netaccess IP UDP DNS * A possible cause of failure could be that the open DNS (147.210.18.138) * is not reachable or down. dns_ans = sr1(IP(dst="147.210.18.138")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.com"))) dns_ans ############ ############ + More complex tests = Implicit logic ~ IP TCP a=IP(ttl=(5,10))/TCP(dport=[80,443]) [p for p in a] len(_) == 12 ############ ############ + Real usages = Port scan ~ netaccess IP TCP ans,unans=sr(IP(dst="www.google.com/30")/TCP(dport=[80,443]),timeout=2) ans.make_table(lambda (s,r): (s.dst, s.dport, r.sprintf("{TCP:%TCP.flags%}{ICMP:%ICMP.code%}"))) = Traceroute function ~ netaccess * Let's test traceroute traceroute("www.slashdot.org") ans,unans=_ = Result manipulation ~ netaccess ans.nsummary() s,r=ans[0] s.show() s.show(2) = DNS packet manipulation ~ netaccess DNS * We have to recalculate IP and UDP length because * DNS is not able to reassemble correctly dns_ans.show() del(dns_ans[IP].len) del(dns_ans[UDP].len) dns_ans.show2() dns_ans[DNS].an.show() DNS in IP(str(dns_ans)) = Arping ~ netaccess * This test assumes the local network is a /24. This is bad. conf.route.route("0.0.0.0")[2] arping(_+"/24")