1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package icmp_test
6
7import (
8	"fmt"
9	"net"
10	"reflect"
11	"testing"
12
13	"golang.org/x/net/icmp"
14	"golang.org/x/net/internal/iana"
15	"golang.org/x/net/ipv4"
16	"golang.org/x/net/ipv6"
17)
18
19var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
20	{
21		Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
22		Body: &icmp.DstUnreach{
23			Data: []byte("ERROR-INVOKING-PACKET"),
24			Extensions: []icmp.Extension{
25				&icmp.MPLSLabelStack{
26					Class: 1,
27					Type:  1,
28					Labels: []icmp.MPLSLabel{
29						{
30							Label: 16014,
31							TC:    0x4,
32							S:     true,
33							TTL:   255,
34						},
35					},
36				},
37				&icmp.InterfaceInfo{
38					Class: 2,
39					Type:  0x0f,
40					Interface: &net.Interface{
41						Index: 15,
42						Name:  "en101",
43						MTU:   8192,
44					},
45					Addr: &net.IPAddr{
46						IP: net.IPv4(192, 168, 0, 1).To4(),
47					},
48				},
49			},
50		},
51	},
52	{
53		Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
54		Body: &icmp.TimeExceeded{
55			Data: []byte("ERROR-INVOKING-PACKET"),
56			Extensions: []icmp.Extension{
57				&icmp.InterfaceInfo{
58					Class: 2,
59					Type:  0x0f,
60					Interface: &net.Interface{
61						Index: 15,
62						Name:  "en101",
63						MTU:   8192,
64					},
65					Addr: &net.IPAddr{
66						IP: net.IPv4(192, 168, 0, 1).To4(),
67					},
68				},
69				&icmp.MPLSLabelStack{
70					Class: 1,
71					Type:  1,
72					Labels: []icmp.MPLSLabel{
73						{
74							Label: 16014,
75							TC:    0x4,
76							S:     true,
77							TTL:   255,
78						},
79					},
80				},
81			},
82		},
83	},
84	{
85		Type: ipv4.ICMPTypeParameterProblem, Code: 2,
86		Body: &icmp.ParamProb{
87			Pointer: 8,
88			Data:    []byte("ERROR-INVOKING-PACKET"),
89			Extensions: []icmp.Extension{
90				&icmp.MPLSLabelStack{
91					Class: 1,
92					Type:  1,
93					Labels: []icmp.MPLSLabel{
94						{
95							Label: 16014,
96							TC:    0x4,
97							S:     true,
98							TTL:   255,
99						},
100					},
101				},
102				&icmp.InterfaceInfo{
103					Class: 2,
104					Type:  0x0f,
105					Interface: &net.Interface{
106						Index: 15,
107						Name:  "en101",
108						MTU:   8192,
109					},
110					Addr: &net.IPAddr{
111						IP: net.IPv4(192, 168, 0, 1).To4(),
112					},
113				},
114				&icmp.InterfaceInfo{
115					Class: 2,
116					Type:  0x2f,
117					Interface: &net.Interface{
118						Index: 16,
119						Name:  "en102",
120						MTU:   8192,
121					},
122					Addr: &net.IPAddr{
123						IP: net.IPv4(192, 168, 0, 2).To4(),
124					},
125				},
126			},
127		},
128	},
129}
130
131func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
132	for i, tt := range marshalAndParseMultipartMessageForIPv4Tests {
133		b, err := tt.Marshal(nil)
134		if err != nil {
135			t.Fatal(err)
136		}
137		if b[5] != 32 {
138			t.Errorf("#%v: got %v; want 32", i, b[5])
139		}
140		m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
141		if err != nil {
142			t.Fatal(err)
143		}
144		if m.Type != tt.Type || m.Code != tt.Code {
145			t.Errorf("#%v: got %v; want %v", i, m, &tt)
146		}
147		switch m.Type {
148		case ipv4.ICMPTypeDestinationUnreachable:
149			got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
150			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
151				t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
152			}
153			if len(got.Data) != 128 {
154				t.Errorf("#%v: got %v; want 128", i, len(got.Data))
155			}
156		case ipv4.ICMPTypeTimeExceeded:
157			got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
158			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
159				t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
160			}
161			if len(got.Data) != 128 {
162				t.Errorf("#%v: got %v; want 128", i, len(got.Data))
163			}
164		case ipv4.ICMPTypeParameterProblem:
165			got, want := m.Body.(*icmp.ParamProb), tt.Body.(*icmp.ParamProb)
166			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
167				t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
168			}
169			if len(got.Data) != 128 {
170				t.Errorf("#%v: got %v; want 128", i, len(got.Data))
171			}
172		}
173	}
174}
175
176var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
177	{
178		Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
179		Body: &icmp.DstUnreach{
180			Data: []byte("ERROR-INVOKING-PACKET"),
181			Extensions: []icmp.Extension{
182				&icmp.MPLSLabelStack{
183					Class: 1,
184					Type:  1,
185					Labels: []icmp.MPLSLabel{
186						{
187							Label: 16014,
188							TC:    0x4,
189							S:     true,
190							TTL:   255,
191						},
192					},
193				},
194				&icmp.InterfaceInfo{
195					Class: 2,
196					Type:  0x0f,
197					Interface: &net.Interface{
198						Index: 15,
199						Name:  "en101",
200						MTU:   8192,
201					},
202					Addr: &net.IPAddr{
203						IP:   net.ParseIP("fe80::1"),
204						Zone: "en101",
205					},
206				},
207			},
208		},
209	},
210	{
211		Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
212		Body: &icmp.TimeExceeded{
213			Data: []byte("ERROR-INVOKING-PACKET"),
214			Extensions: []icmp.Extension{
215				&icmp.InterfaceInfo{
216					Class: 2,
217					Type:  0x0f,
218					Interface: &net.Interface{
219						Index: 15,
220						Name:  "en101",
221						MTU:   8192,
222					},
223					Addr: &net.IPAddr{
224						IP:   net.ParseIP("fe80::1"),
225						Zone: "en101",
226					},
227				},
228				&icmp.MPLSLabelStack{
229					Class: 1,
230					Type:  1,
231					Labels: []icmp.MPLSLabel{
232						{
233							Label: 16014,
234							TC:    0x4,
235							S:     true,
236							TTL:   255,
237						},
238					},
239				},
240				&icmp.InterfaceInfo{
241					Class: 2,
242					Type:  0x2f,
243					Interface: &net.Interface{
244						Index: 16,
245						Name:  "en102",
246						MTU:   8192,
247					},
248					Addr: &net.IPAddr{
249						IP:   net.ParseIP("fe80::1"),
250						Zone: "en102",
251					},
252				},
253			},
254		},
255	},
256}
257
258func TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) {
259	pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
260	for i, tt := range marshalAndParseMultipartMessageForIPv6Tests {
261		for _, psh := range [][]byte{pshicmp, nil} {
262			b, err := tt.Marshal(psh)
263			if err != nil {
264				t.Fatal(err)
265			}
266			if b[4] != 16 {
267				t.Errorf("#%v: got %v; want 16", i, b[4])
268			}
269			m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
270			if err != nil {
271				t.Fatal(err)
272			}
273			if m.Type != tt.Type || m.Code != tt.Code {
274				t.Errorf("#%v: got %v; want %v", i, m, &tt)
275			}
276			switch m.Type {
277			case ipv6.ICMPTypeDestinationUnreachable:
278				got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
279				if !reflect.DeepEqual(got.Extensions, want.Extensions) {
280					t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
281				}
282				if len(got.Data) != 128 {
283					t.Errorf("#%v: got %v; want 128", i, len(got.Data))
284				}
285			case ipv6.ICMPTypeTimeExceeded:
286				got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
287				if !reflect.DeepEqual(got.Extensions, want.Extensions) {
288					t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
289				}
290				if len(got.Data) != 128 {
291					t.Errorf("#%v: got %v; want 128", i, len(got.Data))
292				}
293			}
294		}
295	}
296}
297
298func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string {
299	var s string
300	for j, got := range gotExts {
301		switch got := got.(type) {
302		case *icmp.MPLSLabelStack:
303			want := wantExts[j].(*icmp.MPLSLabelStack)
304			if !reflect.DeepEqual(got, want) {
305				s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want)
306			}
307		case *icmp.InterfaceInfo:
308			want := wantExts[j].(*icmp.InterfaceInfo)
309			if !reflect.DeepEqual(got, want) {
310				s += fmt.Sprintf("#%v/%v: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, j, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
311			}
312		}
313	}
314	return s[:len(s)-1]
315}
316
317var multipartMessageBodyLenTests = []struct {
318	proto int
319	in    icmp.MessageBody
320	out   int
321}{
322	{
323		iana.ProtocolICMP,
324		&icmp.DstUnreach{
325			Data: make([]byte, ipv4.HeaderLen),
326		},
327		4 + ipv4.HeaderLen, // unused and original datagram
328	},
329	{
330		iana.ProtocolICMP,
331		&icmp.TimeExceeded{
332			Data: make([]byte, ipv4.HeaderLen),
333		},
334		4 + ipv4.HeaderLen, // unused and original datagram
335	},
336	{
337		iana.ProtocolICMP,
338		&icmp.ParamProb{
339			Data: make([]byte, ipv4.HeaderLen),
340		},
341		4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
342	},
343
344	{
345		iana.ProtocolICMP,
346		&icmp.ParamProb{
347			Data: make([]byte, ipv4.HeaderLen),
348			Extensions: []icmp.Extension{
349				&icmp.MPLSLabelStack{},
350			},
351		},
352		4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
353	},
354	{
355		iana.ProtocolICMP,
356		&icmp.ParamProb{
357			Data: make([]byte, 128),
358			Extensions: []icmp.Extension{
359				&icmp.MPLSLabelStack{},
360			},
361		},
362		4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
363	},
364	{
365		iana.ProtocolICMP,
366		&icmp.ParamProb{
367			Data: make([]byte, 129),
368			Extensions: []icmp.Extension{
369				&icmp.MPLSLabelStack{},
370			},
371		},
372		4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
373	},
374
375	{
376		iana.ProtocolIPv6ICMP,
377		&icmp.DstUnreach{
378			Data: make([]byte, ipv6.HeaderLen),
379		},
380		4 + ipv6.HeaderLen, // unused and original datagram
381	},
382	{
383		iana.ProtocolIPv6ICMP,
384		&icmp.PacketTooBig{
385			Data: make([]byte, ipv6.HeaderLen),
386		},
387		4 + ipv6.HeaderLen, // mtu and original datagram
388	},
389	{
390		iana.ProtocolIPv6ICMP,
391		&icmp.TimeExceeded{
392			Data: make([]byte, ipv6.HeaderLen),
393		},
394		4 + ipv6.HeaderLen, // unused and original datagram
395	},
396	{
397		iana.ProtocolIPv6ICMP,
398		&icmp.ParamProb{
399			Data: make([]byte, ipv6.HeaderLen),
400		},
401		4 + ipv6.HeaderLen, // pointer and original datagram
402	},
403
404	{
405		iana.ProtocolIPv6ICMP,
406		&icmp.DstUnreach{
407			Data: make([]byte, 127),
408			Extensions: []icmp.Extension{
409				&icmp.MPLSLabelStack{},
410			},
411		},
412		4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
413	},
414	{
415		iana.ProtocolIPv6ICMP,
416		&icmp.DstUnreach{
417			Data: make([]byte, 128),
418			Extensions: []icmp.Extension{
419				&icmp.MPLSLabelStack{},
420			},
421		},
422		4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
423	},
424	{
425		iana.ProtocolIPv6ICMP,
426		&icmp.DstUnreach{
427			Data: make([]byte, 129),
428			Extensions: []icmp.Extension{
429				&icmp.MPLSLabelStack{},
430			},
431		},
432		4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
433	},
434}
435
436func TestMultipartMessageBodyLen(t *testing.T) {
437	for i, tt := range multipartMessageBodyLenTests {
438		if out := tt.in.Len(tt.proto); out != tt.out {
439			t.Errorf("#%d: got %d; want %d", i, out, tt.out)
440		}
441	}
442}
443