1// Copyright 2012 Google, Inc. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree.
6
7// This file tests some of the functionality provided in the ip4.go
8
9package layers
10
11import (
12	"bytes"
13	"encoding/binary"
14	"encoding/hex"
15	"net"
16	"reflect"
17	"testing"
18
19	"github.com/google/gopacket"
20)
21
22// Test the function getIPv4OptionSize when the ipv4 has no options
23func TestGetIPOptLengthNoOpt(t *testing.T) {
24	ip := IPv4{}
25	length := ip.getIPv4OptionSize()
26	if length != 0 {
27		t.Fatalf("Empty option list should have 0 length.  Actual %d", length)
28	}
29}
30
31// Test the function getIPv4OptionSize when the ipv4 has end of list option
32func TestGetIPOptLengthEndOfList(t *testing.T) {
33	ip := IPv4{}
34	ip.Options = append(ip.Options, IPv4Option{OptionType: 0, OptionLength: 1})
35	length := ip.getIPv4OptionSize()
36	if length != 4 {
37		t.Fatalf("After padding, the list should have 4 length.  Actual %d", length)
38	}
39}
40
41// Test the function getIPv4OptionSize when the ipv4 has padding and end of list option
42func TestGetIPOptLengthPaddingEndOfList(t *testing.T) {
43	ip := IPv4{}
44	ip.Options = append(ip.Options, IPv4Option{OptionType: 1, OptionLength: 1})
45	ip.Options = append(ip.Options, IPv4Option{OptionType: 0, OptionLength: 1})
46	length := ip.getIPv4OptionSize()
47	if length != 4 {
48		t.Fatalf("After padding, the list should have 4 length.  Actual %d", length)
49	}
50}
51
52// Test the function getIPv4OptionSize when the ipv4 has some non-trivial option and end of list option
53func TestGetIPOptLengthOptionEndOfList(t *testing.T) {
54	ip := IPv4{}
55	someByte := make([]byte, 8)
56	ip.Options = append(ip.Options, IPv4Option{OptionType: 2, OptionLength: 10, OptionData: someByte})
57	ip.Options = append(ip.Options, IPv4Option{OptionType: 0, OptionLength: 1})
58	length := ip.getIPv4OptionSize()
59	if length != 12 {
60		t.Fatalf("The list should have 12 length.  Actual %d", length)
61	}
62}
63
64// Tests that the Options slice is properly reset before parsing new data
65func TestIPOptResetDuringDecoding(t *testing.T) {
66	ip := &IPv4{
67		Options: []IPv4Option{{OptionType: 42, OptionLength: 4, OptionData: make([]byte, 2)}},
68	}
69
70	ipWithoutOptions := &IPv4{
71		SrcIP:    net.IPv4(192, 168, 1, 1),
72		DstIP:    net.IPv4(192, 168, 1, 1),
73		Protocol: IPProtocolTCP,
74	}
75
76	ipBytes, err := serialize(ipWithoutOptions)
77
78	if err != nil {
79		t.Fatalf("Failed to serialize ip layer: %v", err)
80	}
81
82	err = ip.DecodeFromBytes(ipBytes, gopacket.NilDecodeFeedback)
83
84	if err != nil {
85		t.Fatalf("Failed to deserialize ip layer: %v", err)
86	}
87
88	if len(ip.Options) > 0 {
89		t.Fatalf("Options slice has stale data from previous packet")
90	}
91
92}
93
94func serialize(ip *IPv4) ([]byte, error) {
95	buffer := gopacket.NewSerializeBuffer()
96	err := ip.SerializeTo(buffer, gopacket.SerializeOptions{
97		FixLengths:       true,
98		ComputeChecksums: true,
99	})
100	return buffer.Bytes(), err
101}
102
103// Test the function checksum
104func TestChecksum(t *testing.T) {
105	testData := []struct {
106		name   string
107		header string
108		want   string
109	}{{
110		name:   "sum has two carries",
111		header: "4540005800000000ff11ffff0aeb1d070aed8877",
112		want:   "fffe",
113	}, {
114		name:   "wikipedia case",
115		header: "45000073000040004011b861c0a80001c0a800c7",
116		want:   "b861",
117	}}
118
119	for _, test := range testData {
120		bytes, err := hex.DecodeString(test.header)
121		if err != nil {
122			t.Fatalf("Failed to Decode header: %v", err)
123		}
124		wantBytes, err := hex.DecodeString(test.want)
125		if err != nil {
126			t.Fatalf("Failed to decode want checksum: %v", err)
127		}
128
129		if got, want := checksum(bytes), binary.BigEndian.Uint16(wantBytes); got != want {
130			t.Errorf("In test %q, got incorrect checksum: got(%x), want(%x)", test.name, got, want)
131		}
132	}
133}
134
135func TestIPv4InvalidOptionLength(t *testing.T) {
136	// ip4 Packet with option 136 length set to zero
137	b, err := hex.DecodeString("460000705f5b0000ff114e02af2db00295ab7e0f88001234")
138	if err != nil {
139		t.Fatalf("Failed to Decode header: %v", err)
140	}
141	var ip4 IPv4
142	err = ip4.DecodeFromBytes(b, gopacket.NilDecodeFeedback)
143	if err == nil {
144		t.Fatal("Expected 'invalid IP option length' error, but got none.")
145	}
146}
147
148func TestIPv4Options(t *testing.T) {
149	var ip4 IPv4 // reuse ip4 to test reset
150	for _, test := range []struct {
151		packet  string
152		options []IPv4Option
153		padding []byte
154	}{
155		{
156			packet: "4800002803040000fe01c1e0af2db00095ab7e0b820b00000000000000000000",
157			options: []IPv4Option{
158				{
159					OptionType:   130,
160					OptionData:   []byte{0, 0, 0, 0, 0, 0, 0, 0, 0},
161					OptionLength: 11,
162				},
163				{
164					OptionType:   0,
165					OptionLength: 1,
166				},
167			},
168		},
169		{
170			packet: "4900002803040000fe01c1e0af2db00095ab7e0b01820b00000000000000000000010203",
171			options: []IPv4Option{
172				{
173					OptionType:   1,
174					OptionLength: 1,
175				},
176				{
177					OptionType:   130,
178					OptionData:   []byte{0, 0, 0, 0, 0, 0, 0, 0, 0},
179					OptionLength: 11,
180				},
181				{
182					OptionType:   0,
183					OptionLength: 1,
184				},
185			},
186			padding: []byte{1, 2, 3},
187		},
188		{
189			packet: "4800002803040000fe01c1e0af2db00095ab7e0b820c00000000000000000000",
190			options: []IPv4Option{
191				{
192					OptionType:   130,
193					OptionData:   []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
194					OptionLength: 12,
195				},
196			},
197		},
198		{
199			packet: "4900002803040000fe01c1e0af2db00095ab7e0b00820b00000000000000000000010203",
200			options: []IPv4Option{
201				{
202					OptionType:   0,
203					OptionLength: 1,
204				},
205			},
206			padding: []byte{0x82, 0x0b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3},
207		},
208	} {
209		b, err := hex.DecodeString(test.packet)
210		if err != nil {
211			t.Fatalf("Failed to Decode header: %v", err)
212		}
213		err = ip4.DecodeFromBytes(b, gopacket.NilDecodeFeedback)
214		if err != nil {
215			t.Fatal("Unexpected error during decoding:", err)
216		}
217		if !reflect.DeepEqual(ip4.Options, test.options) {
218			t.Fatalf("Options mismatch.\nGot:\n%#v\nExpected:\n%#v\n", ip4.Options, test.options)
219		}
220		if !bytes.Equal(ip4.Padding, test.padding) {
221			t.Fatalf("Padding mismatch.\nGot:\n%#v\nExpected:\n%#v\n", ip4.Padding, test.padding)
222		}
223	}
224}
225