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
6
7import (
8	"fmt"
9	"net"
10	"reflect"
11	"testing"
12
13	"golang.org/x/net/internal/iana"
14	"golang.org/x/net/ipv4"
15	"golang.org/x/net/ipv6"
16)
17
18func TestMarshalAndParseExtension(t *testing.T) {
19	fn := func(t *testing.T, proto int, typ Type, hdr, obj []byte, te Extension) error {
20		b, err := te.Marshal(proto)
21		if err != nil {
22			return err
23		}
24		if !reflect.DeepEqual(b, obj) {
25			return fmt.Errorf("got %#v; want %#v", b, obj)
26		}
27		switch typ {
28		case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
29			exts, l, err := parseExtensions(typ, append(hdr, obj...), 0)
30			if err != nil {
31				return err
32			}
33			if l != 0 {
34				return fmt.Errorf("got %d; want 0", l)
35			}
36			if !reflect.DeepEqual(exts, []Extension{te}) {
37				return fmt.Errorf("got %#v; want %#v", exts[0], te)
38			}
39		default:
40			for i, wire := range []struct {
41				data     []byte // original datagram
42				inlattr  int    // length of padded original datagram, a hint
43				outlattr int    // length of padded original datagram, a want
44				err      error
45			}{
46				{nil, 0, -1, errNoExtension},
47				{make([]byte, 127), 128, -1, errNoExtension},
48
49				{make([]byte, 128), 127, -1, errNoExtension},
50				{make([]byte, 128), 128, -1, errNoExtension},
51				{make([]byte, 128), 129, -1, errNoExtension},
52
53				{append(make([]byte, 128), append(hdr, obj...)...), 127, 128, nil},
54				{append(make([]byte, 128), append(hdr, obj...)...), 128, 128, nil},
55				{append(make([]byte, 128), append(hdr, obj...)...), 129, 128, nil},
56
57				{append(make([]byte, 512), append(hdr, obj...)...), 511, -1, errNoExtension},
58				{append(make([]byte, 512), append(hdr, obj...)...), 512, 512, nil},
59				{append(make([]byte, 512), append(hdr, obj...)...), 513, -1, errNoExtension},
60			} {
61				exts, l, err := parseExtensions(typ, wire.data, wire.inlattr)
62				if err != wire.err {
63					return fmt.Errorf("#%d: got %v; want %v", i, err, wire.err)
64				}
65				if wire.err != nil {
66					continue
67				}
68				if l != wire.outlattr {
69					return fmt.Errorf("#%d: got %d; want %d", i, l, wire.outlattr)
70				}
71				if !reflect.DeepEqual(exts, []Extension{te}) {
72					return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], te)
73				}
74			}
75		}
76		return nil
77	}
78
79	t.Run("MPLSLabelStack", func(t *testing.T) {
80		for _, et := range []struct {
81			proto int
82			typ   Type
83			hdr   []byte
84			obj   []byte
85			ext   Extension
86		}{
87			// MPLS label stack with no label
88			{
89				proto: iana.ProtocolICMP,
90				typ:   ipv4.ICMPTypeDestinationUnreachable,
91				hdr: []byte{
92					0x20, 0x00, 0x00, 0x00,
93				},
94				obj: []byte{
95					0x00, 0x04, 0x01, 0x01,
96				},
97				ext: &MPLSLabelStack{
98					Class: classMPLSLabelStack,
99					Type:  typeIncomingMPLSLabelStack,
100				},
101			},
102			// MPLS label stack with a single label
103			{
104				proto: iana.ProtocolIPv6ICMP,
105				typ:   ipv6.ICMPTypeDestinationUnreachable,
106				hdr: []byte{
107					0x20, 0x00, 0x00, 0x00,
108				},
109				obj: []byte{
110					0x00, 0x08, 0x01, 0x01,
111					0x03, 0xe8, 0xe9, 0xff,
112				},
113				ext: &MPLSLabelStack{
114					Class: classMPLSLabelStack,
115					Type:  typeIncomingMPLSLabelStack,
116					Labels: []MPLSLabel{
117						{
118							Label: 16014,
119							TC:    0x4,
120							S:     true,
121							TTL:   255,
122						},
123					},
124				},
125			},
126			// MPLS label stack with multiple labels
127			{
128				proto: iana.ProtocolICMP,
129				typ:   ipv4.ICMPTypeDestinationUnreachable,
130				hdr: []byte{
131					0x20, 0x00, 0x00, 0x00,
132				},
133				obj: []byte{
134					0x00, 0x0c, 0x01, 0x01,
135					0x03, 0xe8, 0xde, 0xfe,
136					0x03, 0xe8, 0xe1, 0xff,
137				},
138				ext: &MPLSLabelStack{
139					Class: classMPLSLabelStack,
140					Type:  typeIncomingMPLSLabelStack,
141					Labels: []MPLSLabel{
142						{
143							Label: 16013,
144							TC:    0x7,
145							S:     false,
146							TTL:   254,
147						},
148						{
149							Label: 16014,
150							TC:    0,
151							S:     true,
152							TTL:   255,
153						},
154					},
155				},
156			},
157		} {
158			if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
159				t.Error(err)
160			}
161		}
162	})
163	t.Run("InterfaceInfo", func(t *testing.T) {
164		for _, et := range []struct {
165			proto int
166			typ   Type
167			hdr   []byte
168			obj   []byte
169			ext   Extension
170		}{
171			// Interface information with no attribute
172			{
173				proto: iana.ProtocolICMP,
174				typ:   ipv4.ICMPTypeDestinationUnreachable,
175				hdr: []byte{
176					0x20, 0x00, 0x00, 0x00,
177				},
178				obj: []byte{
179					0x00, 0x04, 0x02, 0x00,
180				},
181				ext: &InterfaceInfo{
182					Class: classInterfaceInfo,
183				},
184			},
185			// Interface information with ifIndex and name
186			{
187				proto: iana.ProtocolICMP,
188				typ:   ipv4.ICMPTypeDestinationUnreachable,
189				hdr: []byte{
190					0x20, 0x00, 0x00, 0x00,
191				},
192				obj: []byte{
193					0x00, 0x10, 0x02, 0x0a,
194					0x00, 0x00, 0x00, 0x10,
195					0x08, byte('e'), byte('n'), byte('1'),
196					byte('0'), byte('1'), 0x00, 0x00,
197				},
198				ext: &InterfaceInfo{
199					Class: classInterfaceInfo,
200					Type:  0x0a,
201					Interface: &net.Interface{
202						Index: 16,
203						Name:  "en101",
204					},
205				},
206			},
207			// Interface information with ifIndex, IPAddr, name and MTU
208			{
209				proto: iana.ProtocolIPv6ICMP,
210				typ:   ipv6.ICMPTypeDestinationUnreachable,
211				hdr: []byte{
212					0x20, 0x00, 0x00, 0x00,
213				},
214				obj: []byte{
215					0x00, 0x28, 0x02, 0x0f,
216					0x00, 0x00, 0x00, 0x0f,
217					0x00, 0x02, 0x00, 0x00,
218					0xfe, 0x80, 0x00, 0x00,
219					0x00, 0x00, 0x00, 0x00,
220					0x00, 0x00, 0x00, 0x00,
221					0x00, 0x00, 0x00, 0x01,
222					0x08, byte('e'), byte('n'), byte('1'),
223					byte('0'), byte('1'), 0x00, 0x00,
224					0x00, 0x00, 0x20, 0x00,
225				},
226				ext: &InterfaceInfo{
227					Class: classInterfaceInfo,
228					Type:  0x0f,
229					Interface: &net.Interface{
230						Index: 15,
231						Name:  "en101",
232						MTU:   8192,
233					},
234					Addr: &net.IPAddr{
235						IP:   net.ParseIP("fe80::1"),
236						Zone: "en101",
237					},
238				},
239			},
240		} {
241			if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
242				t.Error(err)
243			}
244		}
245	})
246	t.Run("InterfaceIdent", func(t *testing.T) {
247		for _, et := range []struct {
248			proto int
249			typ   Type
250			hdr   []byte
251			obj   []byte
252			ext   Extension
253		}{
254			// Interface identification by name
255			{
256				proto: iana.ProtocolICMP,
257				typ:   ipv4.ICMPTypeExtendedEchoRequest,
258				hdr: []byte{
259					0x20, 0x00, 0x00, 0x00,
260				},
261				obj: []byte{
262					0x00, 0x0c, 0x03, 0x01,
263					byte('e'), byte('n'), byte('1'), byte('0'),
264					byte('1'), 0x00, 0x00, 0x00,
265				},
266				ext: &InterfaceIdent{
267					Class: classInterfaceIdent,
268					Type:  typeInterfaceByName,
269					Name:  "en101",
270				},
271			},
272			// Interface identification by index
273			{
274				proto: iana.ProtocolIPv6ICMP,
275				typ:   ipv6.ICMPTypeExtendedEchoRequest,
276				hdr: []byte{
277					0x20, 0x00, 0x00, 0x00,
278				},
279				obj: []byte{
280					0x00, 0x08, 0x03, 0x02,
281					0x00, 0x00, 0x03, 0x8f,
282				},
283				ext: &InterfaceIdent{
284					Class: classInterfaceIdent,
285					Type:  typeInterfaceByIndex,
286					Index: 911,
287				},
288			},
289			// Interface identification by address
290			{
291				proto: iana.ProtocolICMP,
292				typ:   ipv4.ICMPTypeExtendedEchoRequest,
293				hdr: []byte{
294					0x20, 0x00, 0x00, 0x00,
295				},
296				obj: []byte{
297					0x00, 0x10, 0x03, 0x03,
298					byte(iana.AddrFamily48bitMAC >> 8), byte(iana.AddrFamily48bitMAC & 0x0f), 0x06, 0x00,
299					0x01, 0x23, 0x45, 0x67,
300					0x89, 0xab, 0x00, 0x00,
301				},
302				ext: &InterfaceIdent{
303					Class: classInterfaceIdent,
304					Type:  typeInterfaceByAddress,
305					AFI:   iana.AddrFamily48bitMAC,
306					Addr:  []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
307				},
308			},
309		} {
310			if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
311				t.Error(err)
312			}
313		}
314	})
315}
316
317func TestParseInterfaceName(t *testing.T) {
318	ifi := InterfaceInfo{Interface: &net.Interface{}}
319	for i, tt := range []struct {
320		b []byte
321		error
322	}{
323		{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
324		{[]byte{4, 'e', 'n', '0'}, nil},
325		{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
326		{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
327	} {
328		if _, err := ifi.parseName(tt.b); err != tt.error {
329			t.Errorf("#%d: got %v; want %v", i, err, tt.error)
330		}
331	}
332}
333