From fa657ad649e06dbd5fc78e31b1424aeb56812df6 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 27 Feb 2016 22:34:18 -0800 Subject: [PATCH] Add a basic regression test for DHCP parsing. --- dhcp/dhcp.go | 35 ++++++++++++++++++ dhcp/dhcp_test.go | 73 ++++++++++++++++++++++++++++++++++++++ dhcp/testdata/dhcp.parsed | 68 +++++++++++++++++++++++++++++++++++ dhcp/testdata/dhcp.pcap | Bin 0 -> 1385 bytes 4 files changed, 176 insertions(+) create mode 100644 dhcp/dhcp_test.go create mode 100644 dhcp/testdata/dhcp.parsed create mode 100644 dhcp/testdata/dhcp.pcap diff --git a/dhcp/dhcp.go b/dhcp/dhcp.go index f1d17d1..cab1ee0 100644 --- a/dhcp/dhcp.go +++ b/dhcp/dhcp.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net" + "sort" ) var magic = []byte{99, 130, 83, 99} @@ -68,6 +69,40 @@ type Packet struct { Options Options } +func (p *Packet) testString() string { + var b bytes.Buffer + bcast := "Unicast" + if p.Broadcast { + bcast = "Broadcast" + } + fmt.Fprintf(&b, `===== +%s + %#v + %s + MAC: %s + ClientIP: %s + YourIP: %s + RelayIP: %s + + BootServerIP: %s + BootServerName: %s + BootFilename: %s + + Options: +`, p.Type, p.TransactionID, bcast, p.HardwareAddr, p.ClientAddr, p.YourAddr, p.RelayAddr, p.BootServerAddr, p.BootServerName, p.BootFilename) + + var opts []int + for n := range p.Options { + opts = append(opts, n) + } + sort.Ints(opts) + for _, n := range opts { + fmt.Fprintf(&b, " %d: %#v\n", n, p.Options[n]) + } + b.WriteString("=====\n") + return b.String() +} + // Marshal returns the wire encoding of p. func (p *Packet) Marshal() ([]byte, error) { if len(p.TransactionID) != 4 { diff --git a/dhcp/dhcp_test.go b/dhcp/dhcp_test.go new file mode 100644 index 0000000..1e21df5 --- /dev/null +++ b/dhcp/dhcp_test.go @@ -0,0 +1,73 @@ +package dhcp + +import ( + "bytes" + "errors" + "io/ioutil" + "os" + "testing" + + "go.universe.tf/netboot/pcap" +) + +func udpFromPcap(fname string) ([][]byte, error) { + f, err := os.Open(fname) + if err != nil { + return nil, err + } + r, err := pcap.NewReader(f) + if err != nil { + return nil, err + } + + if r.LinkType != pcap.LinkEthernet { + return nil, errors.New("Pcap packets are not ethernet") + } + + ret := [][]byte{} + for r.Next() { + // Assume here that the packets are UDPv4, and just chop off + // the headers in front of the UDP payload + pkt := r.Packet() + hdrLen := 14 // Ethernet header + hdrLen += int(pkt.Bytes[hdrLen]&0xF) * 4 // IP header + hdrLen += 8 // UDP header + ret = append(ret, pkt.Bytes[hdrLen:]) + } + if r.Err() != nil { + return nil, r.Err() + } + + return ret, nil +} + +func TestParse(t *testing.T) { + rawPkts, err := udpFromPcap("testdata/dhcp.pcap") + if err != nil { + t.Fatalf("Getting test packets from pcap: %s", err) + } + + var pkts bytes.Buffer + for i, rawPkt := range rawPkts { + pkt, err := Unmarshal(rawPkt) + if err != nil { + t.Fatalf("Parsing DHCP packet #%d: %s", i+1, err) + } + pkts.WriteString(pkt.testString()) + } + + expectedFile := "testdata/dhcp.parsed" + expected, err := ioutil.ReadFile(expectedFile) + if err != nil { + t.Fatalf("Reading expected file: %s", err) + } + + if pkts.String() != string(expected) { + if os.Getenv("UPDATE_TESTDATA") != "" { + ioutil.WriteFile(expectedFile, pkts.Bytes(), 0644) + t.Errorf("dhcp.pcap didn't decode to dhcp.parsed (updated dhcp.parsed)") + } else { + t.Fatalf("dhcp.pcap didn't decode to dhcp.parsed (rerun with UPDATE_TESTDATA=1 to get diff)") + } + } +} diff --git a/dhcp/testdata/dhcp.parsed b/dhcp/testdata/dhcp.parsed new file mode 100644 index 0000000..014621a --- /dev/null +++ b/dhcp/testdata/dhcp.parsed @@ -0,0 +1,68 @@ +===== +DHCPDISCOVER + "\x9bN\x05W" + Broadcast + MAC: d0:50:99:4e:05:57 + ClientIP: 0.0.0.0 + YourIP: 0.0.0.0 + RelayIP: 0.0.0.0 + + BootServerIP: 0.0.0.0 + BootServerName: + BootFilename: + + Options: + 53: []byte{0x1} + 55: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xb, 0xc, 0xd, 0xf, 0x10, 0x11, 0x12, 0x16, 0x17, 0x1c, 0x28, 0x29, 0x2a, 0x2b, 0x32, 0x33, 0x36, 0x3a, 0x3b, 0x3c, 0x42, 0x43, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87} + 57: []byte{0x4, 0xec} + 60: []byte{0x50, 0x58, 0x45, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3a, 0x41, 0x72, 0x63, 0x68, 0x3a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3a, 0x55, 0x4e, 0x44, 0x49, 0x3a, 0x30, 0x30, 0x32, 0x30, 0x30, 0x31} + 93: []byte{0x0, 0x0} + 94: []byte{0x1, 0x2, 0x1} + 97: []byte{0x0, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0, 0x5, 0x0, 0x6, 0x0, 0x7, 0x0, 0x8, 0x0, 0x9} +===== +===== +DHCPOFFER + "\x9bN\x05W" + Broadcast + MAC: d0:50:99:4e:05:57 + ClientIP: 0.0.0.0 + YourIP: 0.0.0.0 + RelayIP: 0.0.0.0 + + BootServerIP: 0.0.0.0 + BootServerName: + BootFilename: + + Options: + 43: []byte{0x6, 0x1, 0x3, 0x8, 0x7, 0x80, 0x0, 0x1, 0xc0, 0xa8, 0x10, 0xa, 0x9, 0xc, 0x80, 0x0, 0x9, 0x50, 0x69, 0x78, 0x69, 0x65, 0x63, 0x6f, 0x72, 0x65, 0xa, 0xa, 0x0, 0x50, 0x69, 0x78, 0x69, 0x65, 0x63, 0x6f, 0x72, 0x65, 0xff} + 53: []byte{0x2} + 54: []byte{0xc0, 0xa8, 0x10, 0xa} + 60: []byte{0x50, 0x58, 0x45, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74} + 97: []byte{0x0, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0, 0x5, 0x0, 0x6, 0x0, 0x7, 0x0, 0x8, 0x0, 0x9} +===== +===== +DHCPOFFER + "\x9bN\x05W" + Broadcast + MAC: d0:50:99:4e:05:57 + ClientIP: 0.0.0.0 + YourIP: 192.168.16.12 + RelayIP: 0.0.0.0 + + BootServerIP: 192.168.16.1 + BootServerName: + BootFilename: + + Options: + 1: []byte{0xff, 0xff, 0xff, 0x0} + 3: []byte{0xc0, 0xa8, 0x10, 0x1} + 6: []byte{0xc0, 0xa8, 0x10, 0x1} + 12: []byte{0x63, 0x6f, 0x72, 0x65, 0x30, 0x31} + 15: []byte{0x68, 0x6f, 0x6d, 0x65, 0x2e, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x65, 0x2e, 0x74, 0x66} + 28: []byte{0xc0, 0xa8, 0x10, 0xff} + 51: []byte{0x0, 0x0, 0xe, 0x10} + 53: []byte{0x2} + 54: []byte{0xc0, 0xa8, 0x10, 0x1} + 58: []byte{0x0, 0x0, 0x7, 0x8} + 59: []byte{0x0, 0x0, 0xc, 0x4e} +===== diff --git a/dhcp/testdata/dhcp.pcap b/dhcp/testdata/dhcp.pcap new file mode 100644 index 0000000000000000000000000000000000000000..387e87f67d25cf2b207dfc3ce02f3685d3eefaee GIT binary patch literal 1385 zcmeaZ*|d;}frSA9SQr?AoMn?Pg%#`kVe(^QU;yF&U~nN|rXOoK2ZJjElLJVZh~Sd7 zAe|sd1{VfrCLK#TMn*P<*+5kc91S2T6adkWA~d8q$xXq@ri_f{DvV6bEUawYJiPn@ zfECfdS6Sz{bGNz`?*73k=;j zW=1AP8-;)fSLd9})VvZa$D-s6D+3U)3iWgG1Tu{b3=IDd>A*sAJScUYjbLQX2Bskp zhNP}@&#T|Neg~y4#@JP94h#;0`^pcj5a0r*E@uW8M$bz;Ot93&(12tfggHD@7n2za zC=hHo;i&^lBGOi8V`S!FZ(v{qspaHpVBieMtjJ7F&M!*k;$nca|NjRCy~tNa<_uu4 zgD@oMgF+Z)Xr}^${s3cCf(`>P=-vMTO=5%xz1z%Ac+f*LHc*}iqy`lD!#U^~je*4@ zpMVvRWaqF3l01HlEWm(akO4)^e`XNL$Ob0)1v2t;Q}s&oGRsnnic|GU(st<8 literal 0 HcmV?d00001