1// Copyright 2011 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 net
6
7import (
8	"fmt"
9	"reflect"
10	"runtime"
11	"testing"
12)
13
14// loopbackInterface returns an available logical network interface
15// for loopback tests. It returns nil if no suitable interface is
16// found.
17func loopbackInterface() *Interface {
18	ift, err := Interfaces()
19	if err != nil {
20		return nil
21	}
22	for _, ifi := range ift {
23		if ifi.Flags&FlagLoopback != 0 && ifi.Flags&FlagUp != 0 {
24			return &ifi
25		}
26	}
27	return nil
28}
29
30// ipv6LinkLocalUnicastAddr returns an IPv6 link-local unicast address
31// on the given network interface for tests. It returns "" if no
32// suitable address is found.
33func ipv6LinkLocalUnicastAddr(ifi *Interface) string {
34	if ifi == nil {
35		return ""
36	}
37	ifat, err := ifi.Addrs()
38	if err != nil {
39		return ""
40	}
41	for _, ifa := range ifat {
42		if ifa, ok := ifa.(*IPNet); ok {
43			if ifa.IP.To4() == nil && ifa.IP.IsLinkLocalUnicast() {
44				return ifa.IP.String()
45			}
46		}
47	}
48	return ""
49}
50
51func TestInterfaces(t *testing.T) {
52	ift, err := Interfaces()
53	if err != nil {
54		t.Fatal(err)
55	}
56	for _, ifi := range ift {
57		ifxi, err := InterfaceByIndex(ifi.Index)
58		if err != nil {
59			t.Fatal(err)
60		}
61		switch runtime.GOOS {
62		case "solaris", "illumos":
63			if ifxi.Index != ifi.Index {
64				t.Errorf("got %v; want %v", ifxi, ifi)
65			}
66		default:
67			if !reflect.DeepEqual(ifxi, &ifi) {
68				t.Errorf("got %v; want %v", ifxi, ifi)
69			}
70		}
71		ifxn, err := InterfaceByName(ifi.Name)
72		if err != nil {
73			t.Fatal(err)
74		}
75		if !reflect.DeepEqual(ifxn, &ifi) {
76			t.Errorf("got %v; want %v", ifxn, ifi)
77		}
78		t.Logf("%s: flags=%v index=%d mtu=%d hwaddr=%v", ifi.Name, ifi.Flags, ifi.Index, ifi.MTU, ifi.HardwareAddr)
79	}
80}
81
82func TestInterfaceAddrs(t *testing.T) {
83	ift, err := Interfaces()
84	if err != nil {
85		t.Fatal(err)
86	}
87	ifStats := interfaceStats(ift)
88	ifat, err := InterfaceAddrs()
89	if err != nil {
90		t.Fatal(err)
91	}
92	uniStats, err := validateInterfaceUnicastAddrs(ifat)
93	if err != nil {
94		t.Fatal(err)
95	}
96	if err := checkUnicastStats(ifStats, uniStats); err != nil {
97		t.Fatal(err)
98	}
99}
100
101func TestInterfaceUnicastAddrs(t *testing.T) {
102	ift, err := Interfaces()
103	if err != nil {
104		t.Fatal(err)
105	}
106	ifStats := interfaceStats(ift)
107	if err != nil {
108		t.Fatal(err)
109	}
110	var uniStats routeStats
111	for _, ifi := range ift {
112		ifat, err := ifi.Addrs()
113		if err != nil {
114			t.Fatal(ifi, err)
115		}
116		stats, err := validateInterfaceUnicastAddrs(ifat)
117		if err != nil {
118			t.Fatal(ifi, err)
119		}
120		uniStats.ipv4 += stats.ipv4
121		uniStats.ipv6 += stats.ipv6
122	}
123	if err := checkUnicastStats(ifStats, &uniStats); err != nil {
124		t.Fatal(err)
125	}
126}
127
128func TestInterfaceMulticastAddrs(t *testing.T) {
129	ift, err := Interfaces()
130	if err != nil {
131		t.Fatal(err)
132	}
133	ifStats := interfaceStats(ift)
134	ifat, err := InterfaceAddrs()
135	if err != nil {
136		t.Fatal(err)
137	}
138	uniStats, err := validateInterfaceUnicastAddrs(ifat)
139	if err != nil {
140		t.Fatal(err)
141	}
142	var multiStats routeStats
143	for _, ifi := range ift {
144		ifmat, err := ifi.MulticastAddrs()
145		if err != nil {
146			t.Fatal(ifi, err)
147		}
148		stats, err := validateInterfaceMulticastAddrs(ifmat)
149		if err != nil {
150			t.Fatal(ifi, err)
151		}
152		multiStats.ipv4 += stats.ipv4
153		multiStats.ipv6 += stats.ipv6
154	}
155	if err := checkMulticastStats(ifStats, uniStats, &multiStats); err != nil {
156		t.Fatal(err)
157	}
158}
159
160type ifStats struct {
161	loop  int // # of active loopback interfaces
162	other int // # of active other interfaces
163}
164
165func interfaceStats(ift []Interface) *ifStats {
166	var stats ifStats
167	for _, ifi := range ift {
168		if ifi.Flags&FlagUp != 0 {
169			if ifi.Flags&FlagLoopback != 0 {
170				stats.loop++
171			} else {
172				stats.other++
173			}
174		}
175	}
176	return &stats
177}
178
179type routeStats struct {
180	ipv4, ipv6 int // # of active connected unicast, anycast or multicast routes
181}
182
183func validateInterfaceUnicastAddrs(ifat []Addr) (*routeStats, error) {
184	// Note: BSD variants allow assigning any IPv4/IPv6 address
185	// prefix to IP interface. For example,
186	//   - 0.0.0.0/0 through 255.255.255.255/32
187	//   - ::/0 through ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
188	// In other words, there is no tightly-coupled combination of
189	// interface address prefixes and connected routes.
190	stats := new(routeStats)
191	for _, ifa := range ifat {
192		switch ifa := ifa.(type) {
193		case *IPNet:
194			if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() || ifa.Mask == nil {
195				return nil, fmt.Errorf("unexpected value: %#v", ifa)
196			}
197			if len(ifa.IP) != IPv6len {
198				return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa)
199			}
200			prefixLen, maxPrefixLen := ifa.Mask.Size()
201			if ifa.IP.To4() != nil {
202				if 0 >= prefixLen || prefixLen > 8*IPv4len || maxPrefixLen != 8*IPv4len {
203					return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
204				}
205				if ifa.IP.IsLoopback() && prefixLen < 8 { // see RFC 1122
206					return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
207				}
208				stats.ipv4++
209			}
210			if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
211				if 0 >= prefixLen || prefixLen > 8*IPv6len || maxPrefixLen != 8*IPv6len {
212					return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
213				}
214				if ifa.IP.IsLoopback() && prefixLen != 8*IPv6len { // see RFC 4291
215					return nil, fmt.Errorf("unexpected prefix length: %d/%d for %#v", prefixLen, maxPrefixLen, ifa)
216				}
217				stats.ipv6++
218			}
219		case *IPAddr:
220			if ifa == nil || ifa.IP == nil || ifa.IP.IsMulticast() {
221				return nil, fmt.Errorf("unexpected value: %#v", ifa)
222			}
223			if len(ifa.IP) != IPv6len {
224				return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa)
225			}
226			if ifa.IP.To4() != nil {
227				stats.ipv4++
228			}
229			if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
230				stats.ipv6++
231			}
232		default:
233			return nil, fmt.Errorf("unexpected type: %T", ifa)
234		}
235	}
236	return stats, nil
237}
238
239func validateInterfaceMulticastAddrs(ifat []Addr) (*routeStats, error) {
240	stats := new(routeStats)
241	for _, ifa := range ifat {
242		switch ifa := ifa.(type) {
243		case *IPAddr:
244			if ifa == nil || ifa.IP == nil || ifa.IP.IsUnspecified() || !ifa.IP.IsMulticast() {
245				return nil, fmt.Errorf("unexpected value: %#v", ifa)
246			}
247			if len(ifa.IP) != IPv6len {
248				return nil, fmt.Errorf("should be internal representation either IPv6 or IPv4-mapped IPv6 address: %#v", ifa)
249			}
250			if ifa.IP.To4() != nil {
251				stats.ipv4++
252			}
253			if ifa.IP.To16() != nil && ifa.IP.To4() == nil {
254				stats.ipv6++
255			}
256		default:
257			return nil, fmt.Errorf("unexpected type: %T", ifa)
258		}
259	}
260	return stats, nil
261}
262
263func checkUnicastStats(ifStats *ifStats, uniStats *routeStats) error {
264	// Test the existence of connected unicast routes for IPv4.
265	if supportsIPv4() && ifStats.loop+ifStats.other > 0 && uniStats.ipv4 == 0 {
266		return fmt.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats)
267	}
268	// Test the existence of connected unicast routes for IPv6.
269	// We can assume the existence of ::1/128 when at least one
270	// loopback interface is installed.
271	if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 == 0 {
272		return fmt.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats)
273	}
274	return nil
275}
276
277func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) error {
278	switch runtime.GOOS {
279	case "aix", "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "illumos":
280	default:
281		// Test the existence of connected multicast route
282		// clones for IPv4. Unlike IPv6, IPv4 multicast
283		// capability is not a mandatory feature, and so IPv4
284		// multicast validation is ignored and we only check
285		// IPv6 below.
286		//
287		// Test the existence of connected multicast route
288		// clones for IPv6. Some platform never uses loopback
289		// interface as the nexthop for multicast routing.
290		// We can assume the existence of connected multicast
291		// route clones when at least two connected unicast
292		// routes, ::1/128 and other, are installed.
293		if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 > 1 && multiStats.ipv6 == 0 {
294			return fmt.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v, %+v, %+v", ifStats, uniStats, multiStats)
295		}
296	}
297	return nil
298}
299
300func BenchmarkInterfaces(b *testing.B) {
301	b.ReportAllocs()
302	testHookUninstaller.Do(uninstallTestHooks)
303
304	for i := 0; i < b.N; i++ {
305		if _, err := Interfaces(); err != nil {
306			b.Fatal(err)
307		}
308	}
309}
310
311func BenchmarkInterfaceByIndex(b *testing.B) {
312	b.ReportAllocs()
313	testHookUninstaller.Do(uninstallTestHooks)
314
315	ifi := loopbackInterface()
316	if ifi == nil {
317		b.Skip("loopback interface not found")
318	}
319	for i := 0; i < b.N; i++ {
320		if _, err := InterfaceByIndex(ifi.Index); err != nil {
321			b.Fatal(err)
322		}
323	}
324}
325
326func BenchmarkInterfaceByName(b *testing.B) {
327	b.ReportAllocs()
328	testHookUninstaller.Do(uninstallTestHooks)
329
330	ifi := loopbackInterface()
331	if ifi == nil {
332		b.Skip("loopback interface not found")
333	}
334	for i := 0; i < b.N; i++ {
335		if _, err := InterfaceByName(ifi.Name); err != nil {
336			b.Fatal(err)
337		}
338	}
339}
340
341func BenchmarkInterfaceAddrs(b *testing.B) {
342	b.ReportAllocs()
343	testHookUninstaller.Do(uninstallTestHooks)
344
345	for i := 0; i < b.N; i++ {
346		if _, err := InterfaceAddrs(); err != nil {
347			b.Fatal(err)
348		}
349	}
350}
351
352func BenchmarkInterfacesAndAddrs(b *testing.B) {
353	b.ReportAllocs()
354	testHookUninstaller.Do(uninstallTestHooks)
355
356	ifi := loopbackInterface()
357	if ifi == nil {
358		b.Skip("loopback interface not found")
359	}
360	for i := 0; i < b.N; i++ {
361		if _, err := ifi.Addrs(); err != nil {
362			b.Fatal(err)
363		}
364	}
365}
366
367func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
368	b.ReportAllocs()
369	testHookUninstaller.Do(uninstallTestHooks)
370
371	ifi := loopbackInterface()
372	if ifi == nil {
373		b.Skip("loopback interface not found")
374	}
375	for i := 0; i < b.N; i++ {
376		if _, err := ifi.MulticastAddrs(); err != nil {
377			b.Fatal(err)
378		}
379	}
380}
381