1// +build linux
2
3package netlink
4
5import (
6	"net"
7	"os"
8	"testing"
9	"time"
10
11	"golang.org/x/sys/unix"
12)
13
14func TestAddrAdd(t *testing.T) {
15	DoTestAddr(t, AddrAdd)
16}
17
18func TestAddrReplace(t *testing.T) {
19	DoTestAddr(t, AddrReplace)
20}
21
22func DoTestAddr(t *testing.T, FunctionUndertest func(Link, *Addr) error) {
23	if os.Getenv("TRAVIS_BUILD_DIR") != "" {
24		t.Skipf("Fails in travis with: addr_test.go:68: Address flags not set properly, got=0, expected=128")
25	}
26	// TODO: IFA_F_PERMANENT does not seem to be set by default on older kernels?
27	var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)}
28	var peer = &net.IPNet{IP: net.IPv4(127, 0, 0, 3), Mask: net.CIDRMask(24, 32)}
29	var addrTests = []struct {
30		addr     *Addr
31		expected *Addr
32	}{
33		{
34			&Addr{IPNet: address},
35			&Addr{IPNet: address, Label: "lo", Scope: unix.RT_SCOPE_UNIVERSE, Flags: unix.IFA_F_PERMANENT},
36		},
37		{
38			&Addr{IPNet: address, Label: "local"},
39			&Addr{IPNet: address, Label: "local", Scope: unix.RT_SCOPE_UNIVERSE, Flags: unix.IFA_F_PERMANENT},
40		},
41		{
42			&Addr{IPNet: address, Flags: unix.IFA_F_OPTIMISTIC},
43			&Addr{IPNet: address, Label: "lo", Flags: unix.IFA_F_OPTIMISTIC | unix.IFA_F_PERMANENT, Scope: unix.RT_SCOPE_UNIVERSE},
44		},
45		{
46			&Addr{IPNet: address, Flags: unix.IFA_F_OPTIMISTIC | unix.IFA_F_DADFAILED},
47			&Addr{IPNet: address, Label: "lo", Flags: unix.IFA_F_OPTIMISTIC | unix.IFA_F_DADFAILED | unix.IFA_F_PERMANENT, Scope: unix.RT_SCOPE_UNIVERSE},
48		},
49		{
50			&Addr{IPNet: address, Scope: unix.RT_SCOPE_NOWHERE},
51			&Addr{IPNet: address, Label: "lo", Flags: unix.IFA_F_PERMANENT, Scope: unix.RT_SCOPE_NOWHERE},
52		},
53		{
54			&Addr{IPNet: address, Peer: peer},
55			&Addr{IPNet: address, Peer: peer, Label: "lo", Scope: unix.RT_SCOPE_UNIVERSE, Flags: unix.IFA_F_PERMANENT},
56		},
57	}
58
59	tearDown := setUpNetlinkTest(t)
60	defer tearDown()
61
62	link, err := LinkByName("lo")
63	if err != nil {
64		t.Fatal(err)
65	}
66
67	for _, tt := range addrTests {
68		if err = FunctionUndertest(link, tt.addr); err != nil {
69			t.Fatal(err)
70		}
71
72		addrs, err := AddrList(link, FAMILY_ALL)
73		if err != nil {
74			t.Fatal(err)
75		}
76
77		if len(addrs) != 1 {
78			t.Fatal("Address not added properly")
79		}
80
81		if !addrs[0].Equal(*tt.expected) {
82			t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected)
83		}
84
85		if addrs[0].Label != tt.expected.Label {
86			t.Fatalf("Address label not set properly, got=%s, expected=%s", addrs[0].Label, tt.expected.Label)
87		}
88
89		if addrs[0].Flags != tt.expected.Flags {
90			t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags)
91		}
92
93		if addrs[0].Scope != tt.expected.Scope {
94			t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope)
95		}
96
97		if tt.expected.Peer != nil {
98			if !addrs[0].PeerEqual(*tt.expected) {
99				t.Fatalf("Peer Address ip no set properly, got=%s, expected=%s", addrs[0].Peer, tt.expected.Peer)
100			}
101		}
102
103		// Pass FAMILY_V4, we should get the same results as FAMILY_ALL
104		addrs, err = AddrList(link, FAMILY_V4)
105		if err != nil {
106			t.Fatal(err)
107		}
108		if len(addrs) != 1 {
109			t.Fatal("Address not added properly")
110		}
111
112		// Pass a wrong family number, we should get nil list
113		addrs, err = AddrList(link, 0x8)
114		if err != nil {
115			t.Fatal(err)
116		}
117
118		if len(addrs) != 0 {
119			t.Fatal("Address not expected")
120		}
121
122		if err = AddrDel(link, tt.addr); err != nil {
123			t.Fatal(err)
124		}
125
126		addrs, err = AddrList(link, FAMILY_ALL)
127		if err != nil {
128			t.Fatal(err)
129		}
130
131		if len(addrs) != 0 {
132			t.Fatal("Address not removed properly")
133		}
134	}
135
136}
137
138func TestAddrAddReplace(t *testing.T) {
139	tearDown := setUpNetlinkTest(t)
140	defer tearDown()
141
142	var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)}
143	var addr = &Addr{IPNet: address}
144
145	link, err := LinkByName("lo")
146	if err != nil {
147		t.Fatal(err)
148	}
149
150	err = AddrAdd(link, addr)
151	if err != nil {
152		t.Fatal(err)
153	}
154
155	addrs, err := AddrList(link, FAMILY_ALL)
156	if err != nil {
157		t.Fatal(err)
158	}
159
160	if len(addrs) != 1 {
161		t.Fatal("Address not added properly")
162	}
163
164	err = AddrAdd(link, addr)
165	if err == nil {
166		t.Fatal("Re-adding address should fail (but succeeded unexpectedly).")
167	}
168
169	err = AddrReplace(link, addr)
170	if err != nil {
171		t.Fatal("Replacing address failed.")
172	}
173
174	addrs, err = AddrList(link, FAMILY_ALL)
175	if err != nil {
176		t.Fatal(err)
177	}
178
179	if len(addrs) != 1 {
180		t.Fatal("Address not added properly")
181	}
182
183	if err = AddrDel(link, addr); err != nil {
184		t.Fatal(err)
185	}
186
187	addrs, err = AddrList(link, FAMILY_ALL)
188	if err != nil {
189		t.Fatal(err)
190	}
191
192	if len(addrs) != 0 {
193		t.Fatal("Address not removed properly")
194	}
195}
196
197func expectAddrUpdate(ch <-chan AddrUpdate, add bool, dst net.IP) bool {
198	for {
199		timeout := time.After(time.Minute)
200		select {
201		case update := <-ch:
202			if update.NewAddr == add && update.LinkAddress.IP.Equal(dst) {
203				return true
204			}
205		case <-timeout:
206			return false
207		}
208	}
209}
210
211func TestAddrSubscribeWithOptions(t *testing.T) {
212	tearDown := setUpNetlinkTest(t)
213	defer tearDown()
214
215	ch := make(chan AddrUpdate)
216	done := make(chan struct{})
217	defer close(done)
218	var lastError error
219	defer func() {
220		if lastError != nil {
221			t.Fatalf("Fatal error received during subscription: %v", lastError)
222		}
223	}()
224	if err := AddrSubscribeWithOptions(ch, done, AddrSubscribeOptions{
225		ErrorCallback: func(err error) {
226			lastError = err
227		},
228	}); err != nil {
229		t.Fatal(err)
230	}
231
232	// get loopback interface
233	link, err := LinkByName("lo")
234	if err != nil {
235		t.Fatal(err)
236	}
237
238	// bring the interface up
239	if err = LinkSetUp(link); err != nil {
240		t.Fatal(err)
241	}
242
243	ip := net.IPv4(127, 0, 0, 1)
244	if !expectAddrUpdate(ch, true, ip) {
245		t.Fatal("Add update not received as expected")
246	}
247}
248
249func TestAddrSubscribeListExisting(t *testing.T) {
250	tearDown := setUpNetlinkTest(t)
251	defer tearDown()
252
253	ch := make(chan AddrUpdate)
254	done := make(chan struct{})
255	defer close(done)
256
257	// get loopback interface
258	link, err := LinkByName("lo")
259	if err != nil {
260		t.Fatal(err)
261	}
262
263	// bring the interface up
264	if err = LinkSetUp(link); err != nil {
265		t.Fatal(err)
266	}
267
268	var lastError error
269	defer func() {
270		if lastError != nil {
271			t.Fatalf("Fatal error received during subscription: %v", lastError)
272		}
273	}()
274	if err := AddrSubscribeWithOptions(ch, done, AddrSubscribeOptions{
275		ErrorCallback: func(err error) {
276			lastError = err
277		},
278		ListExisting: true,
279	}); err != nil {
280		t.Fatal(err)
281	}
282
283	ip := net.IPv4(127, 0, 0, 1)
284	if !expectAddrUpdate(ch, true, ip) {
285		t.Fatal("Add update not received as expected")
286	}
287}
288