1// Copyright 2016 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
5// +build darwin dragonfly freebsd netbsd openbsd
6
7package route
8
9import (
10	"os"
11	"syscall"
12	"testing"
13	"time"
14)
15
16func TestFetchAndParseRIB(t *testing.T) {
17	for _, typ := range []RIBType{sysNET_RT_DUMP, sysNET_RT_IFLIST} {
18		var lastErr error
19		var ms []Message
20		for _, af := range []int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} {
21			rs, err := fetchAndParseRIB(af, typ)
22			if err != nil {
23				lastErr = err
24				continue
25			}
26			ms = append(ms, rs...)
27		}
28		if len(ms) == 0 && lastErr != nil {
29			t.Error(typ, lastErr)
30			continue
31		}
32		ss, err := msgs(ms).validate()
33		if err != nil {
34			t.Error(typ, err)
35			continue
36		}
37		for _, s := range ss {
38			t.Log(typ, s)
39		}
40	}
41}
42
43var (
44	rtmonSock int
45	rtmonErr  error
46)
47
48func init() {
49	// We need to keep rtmonSock alive to avoid treading on
50	// recycled socket descriptors.
51	rtmonSock, rtmonErr = syscall.Socket(sysAF_ROUTE, sysSOCK_RAW, sysAF_UNSPEC)
52}
53
54// TestMonitorAndParseRIB leaks a worker goroutine and a socket
55// descriptor but that's intentional.
56func TestMonitorAndParseRIB(t *testing.T) {
57	if testing.Short() || os.Getuid() != 0 {
58		t.Skip("must be root")
59	}
60
61	if rtmonErr != nil {
62		t.Fatal(rtmonErr)
63	}
64
65	// We suppose that using an IPv4 link-local address and the
66	// dot1Q ID for Token Ring and FDDI doesn't harm anyone.
67	pv := &propVirtual{addr: "169.254.0.1", mask: "255.255.255.0"}
68	if err := pv.configure(1002); err != nil {
69		t.Skip(err)
70	}
71	if err := pv.setup(); err != nil {
72		t.Skip(err)
73	}
74	pv.teardown()
75
76	go func() {
77		b := make([]byte, os.Getpagesize())
78		for {
79			// There's no easy way to unblock this read
80			// call because the routing message exchange
81			// over routing socket is a connectionless
82			// message-oriented protocol, no control plane
83			// for signaling connectivity, and we cannot
84			// use the net package of standard library due
85			// to the lack of support for routing socket
86			// and circular dependency.
87			n, err := syscall.Read(rtmonSock, b)
88			if err != nil {
89				return
90			}
91			ms, err := ParseRIB(0, b[:n])
92			if err != nil {
93				t.Error(err)
94				return
95			}
96			ss, err := msgs(ms).validate()
97			if err != nil {
98				t.Error(err)
99				return
100			}
101			for _, s := range ss {
102				t.Log(s)
103			}
104		}
105	}()
106
107	for _, vid := range []int{1002, 1003, 1004, 1005} {
108		pv := &propVirtual{addr: "169.254.0.1", mask: "255.255.255.0"}
109		if err := pv.configure(vid); err != nil {
110			t.Fatal(err)
111		}
112		if err := pv.setup(); err != nil {
113			t.Fatal(err)
114		}
115		time.Sleep(200 * time.Millisecond)
116		if err := pv.teardown(); err != nil {
117			t.Fatal(err)
118		}
119		time.Sleep(200 * time.Millisecond)
120	}
121}
122
123func TestParseRIBWithFuzz(t *testing.T) {
124	for _, fuzz := range []string{
125		"0\x00\x05\x050000000000000000" +
126			"00000000000000000000" +
127			"00000000000000000000" +
128			"00000000000000000000" +
129			"0000000000000\x02000000" +
130			"00000000",
131		"\x02\x00\x05\f0000000000000000" +
132			"0\x0200000000000000",
133		"\x02\x00\x05\x100000000000000\x1200" +
134			"0\x00\xff\x00",
135		"\x02\x00\x05\f0000000000000000" +
136			"0\x12000\x00\x02\x0000",
137		"\x00\x00\x00\x01\x00",
138		"00000",
139	} {
140		for typ := RIBType(0); typ < 256; typ++ {
141			ParseRIB(typ, []byte(fuzz))
142		}
143	}
144}
145
146func TestRouteMessage(t *testing.T) {
147	s, err := syscall.Socket(sysAF_ROUTE, sysSOCK_RAW, sysAF_UNSPEC)
148	if err != nil {
149		t.Fatal(err)
150	}
151	defer syscall.Close(s)
152
153	var ms []RouteMessage
154	for _, af := range []int{sysAF_INET, sysAF_INET6} {
155		if _, err := fetchAndParseRIB(af, sysNET_RT_DUMP); err != nil {
156			t.Log(err)
157			continue
158		}
159		switch af {
160		case sysAF_INET:
161			ms = append(ms, []RouteMessage{
162				{
163					Type: sysRTM_GET,
164					Addrs: []Addr{
165						sysRTAX_DST:     &Inet4Addr{IP: [4]byte{127, 0, 0, 1}},
166						sysRTAX_GATEWAY: nil,
167						sysRTAX_NETMASK: nil,
168						sysRTAX_GENMASK: nil,
169						sysRTAX_IFP:     &LinkAddr{},
170						sysRTAX_IFA:     &Inet4Addr{},
171						sysRTAX_AUTHOR:  nil,
172						sysRTAX_BRD:     &Inet4Addr{},
173					},
174				},
175				{
176					Type: sysRTM_GET,
177					Addrs: []Addr{
178						sysRTAX_DST: &Inet4Addr{IP: [4]byte{127, 0, 0, 1}},
179					},
180				},
181			}...)
182		case sysAF_INET6:
183			ms = append(ms, []RouteMessage{
184				{
185					Type: sysRTM_GET,
186					Addrs: []Addr{
187						sysRTAX_DST:     &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
188						sysRTAX_GATEWAY: nil,
189						sysRTAX_NETMASK: nil,
190						sysRTAX_GENMASK: nil,
191						sysRTAX_IFP:     &LinkAddr{},
192						sysRTAX_IFA:     &Inet6Addr{},
193						sysRTAX_AUTHOR:  nil,
194						sysRTAX_BRD:     &Inet6Addr{},
195					},
196				},
197				{
198					Type: sysRTM_GET,
199					Addrs: []Addr{
200						sysRTAX_DST: &Inet6Addr{IP: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}},
201					},
202				},
203			}...)
204		}
205	}
206	for i, m := range ms {
207		m.ID = uintptr(os.Getpid())
208		m.Seq = i + 1
209		wb, err := m.Marshal()
210		if err != nil {
211			t.Fatalf("%v: %v", m, err)
212		}
213		if _, err := syscall.Write(s, wb); err != nil {
214			t.Fatalf("%v: %v", m, err)
215		}
216		rb := make([]byte, os.Getpagesize())
217		n, err := syscall.Read(s, rb)
218		if err != nil {
219			t.Fatalf("%v: %v", m, err)
220		}
221		rms, err := ParseRIB(0, rb[:n])
222		if err != nil {
223			t.Fatalf("%v: %v", m, err)
224		}
225		for _, rm := range rms {
226			if rm, ok := rm.(*RouteMessage); ok && rm.Err != nil {
227				t.Errorf("%v: %v", m, rm.Err)
228			}
229		}
230		ss, err := msgs(rms).validate()
231		if err != nil {
232			t.Fatalf("%v: %v", m, err)
233		}
234		for _, s := range ss {
235			t.Log(s)
236		}
237	}
238}
239