1// Copyright 2012 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 ipv4_test
6
7import (
8	"net"
9	"runtime"
10	"testing"
11
12	"golang.org/x/net/ipv4"
13	"golang.org/x/net/nettest"
14)
15
16var udpMultipleGroupListenerTests = []net.Addr{
17	&net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727
18	&net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)},
19	&net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)},
20}
21
22func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
23	switch runtime.GOOS {
24	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
25		t.Skipf("not supported on %s", runtime.GOOS)
26	}
27	if testing.Short() {
28		t.Skip("to avoid external network")
29	}
30
31	for _, gaddr := range udpMultipleGroupListenerTests {
32		c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port
33		if err != nil {
34			t.Fatal(err)
35		}
36		defer c.Close()
37
38		p := ipv4.NewPacketConn(c)
39		var mift []*net.Interface
40
41		ift, err := net.Interfaces()
42		if err != nil {
43			t.Fatal(err)
44		}
45		for i, ifi := range ift {
46			if _, err := nettest.MulticastSource("ip4", &ifi); err != nil {
47				continue
48			}
49			if err := p.JoinGroup(&ifi, gaddr); err != nil {
50				t.Fatal(err)
51			}
52			mift = append(mift, &ift[i])
53		}
54		for _, ifi := range mift {
55			if err := p.LeaveGroup(ifi, gaddr); err != nil {
56				t.Fatal(err)
57			}
58		}
59	}
60}
61
62func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
63	switch runtime.GOOS {
64	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
65		t.Skipf("not supported on %s", runtime.GOOS)
66	}
67	if testing.Short() {
68		t.Skip("to avoid external network")
69	}
70
71	for _, gaddr := range udpMultipleGroupListenerTests {
72		c1, err := net.ListenPacket("udp4", "224.0.0.0:0") // wildcard address with reusable port
73		if err != nil {
74			t.Fatal(err)
75		}
76		defer c1.Close()
77		_, port, err := net.SplitHostPort(c1.LocalAddr().String())
78		if err != nil {
79			t.Fatal(err)
80		}
81		c2, err := net.ListenPacket("udp4", net.JoinHostPort("224.0.0.0", port)) // wildcard address with reusable port
82		if err != nil {
83			t.Fatal(err)
84		}
85		defer c2.Close()
86
87		var ps [2]*ipv4.PacketConn
88		ps[0] = ipv4.NewPacketConn(c1)
89		ps[1] = ipv4.NewPacketConn(c2)
90		var mift []*net.Interface
91
92		ift, err := net.Interfaces()
93		if err != nil {
94			t.Fatal(err)
95		}
96		for i, ifi := range ift {
97			if _, err := nettest.MulticastSource("ip4", &ifi); err != nil {
98				continue
99			}
100			for _, p := range ps {
101				if err := p.JoinGroup(&ifi, gaddr); err != nil {
102					t.Fatal(err)
103				}
104			}
105			mift = append(mift, &ift[i])
106		}
107		for _, ifi := range mift {
108			for _, p := range ps {
109				if err := p.LeaveGroup(ifi, gaddr); err != nil {
110					t.Fatal(err)
111				}
112			}
113		}
114	}
115}
116
117func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
118	switch runtime.GOOS {
119	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
120		t.Skipf("not supported on %s", runtime.GOOS)
121	}
122	if testing.Short() {
123		t.Skip("to avoid external network")
124	}
125
126	gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
127	type ml struct {
128		c   *ipv4.PacketConn
129		ifi *net.Interface
130	}
131	var mlt []*ml
132
133	ift, err := net.Interfaces()
134	if err != nil {
135		t.Fatal(err)
136	}
137	port := "0"
138	for i, ifi := range ift {
139		ip, err := nettest.MulticastSource("ip4", &ifi)
140		if err != nil {
141			continue
142		}
143		c, err := net.ListenPacket("udp4", net.JoinHostPort(ip.String(), port)) // unicast address with non-reusable port
144		if err != nil {
145			// The listen may fail when the serivce is
146			// already in use, but it's fine because the
147			// purpose of this is not to test the
148			// bookkeeping of IP control block inside the
149			// kernel.
150			t.Log(err)
151			continue
152		}
153		defer c.Close()
154		if port == "0" {
155			_, port, err = net.SplitHostPort(c.LocalAddr().String())
156			if err != nil {
157				t.Fatal(err)
158			}
159		}
160		p := ipv4.NewPacketConn(c)
161		if err := p.JoinGroup(&ifi, &gaddr); err != nil {
162			t.Fatal(err)
163		}
164		mlt = append(mlt, &ml{p, &ift[i]})
165	}
166	for _, m := range mlt {
167		if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
168			t.Fatal(err)
169		}
170	}
171}
172
173func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
174	switch runtime.GOOS {
175	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
176		t.Skipf("not supported on %s", runtime.GOOS)
177	}
178	if testing.Short() {
179		t.Skip("to avoid external network")
180	}
181	if !nettest.SupportsRawSocket() {
182		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
183	}
184
185	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address
186	if err != nil {
187		t.Fatal(err)
188	}
189	defer c.Close()
190
191	r, err := ipv4.NewRawConn(c)
192	if err != nil {
193		t.Fatal(err)
194	}
195	gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
196	var mift []*net.Interface
197
198	ift, err := net.Interfaces()
199	if err != nil {
200		t.Fatal(err)
201	}
202	for i, ifi := range ift {
203		if _, err := nettest.MulticastSource("ip4", &ifi); err != nil {
204			continue
205		}
206		if err := r.JoinGroup(&ifi, &gaddr); err != nil {
207			t.Fatal(err)
208		}
209		mift = append(mift, &ift[i])
210	}
211	for _, ifi := range mift {
212		if err := r.LeaveGroup(ifi, &gaddr); err != nil {
213			t.Fatal(err)
214		}
215	}
216}
217
218func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
219	switch runtime.GOOS {
220	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
221		t.Skipf("not supported on %s", runtime.GOOS)
222	}
223	if testing.Short() {
224		t.Skip("to avoid external network")
225	}
226	if !nettest.SupportsRawSocket() {
227		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
228	}
229
230	gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727
231	type ml struct {
232		c   *ipv4.RawConn
233		ifi *net.Interface
234	}
235	var mlt []*ml
236
237	ift, err := net.Interfaces()
238	if err != nil {
239		t.Fatal(err)
240	}
241	for i, ifi := range ift {
242		ip, err := nettest.MulticastSource("ip4", &ifi)
243		if err != nil {
244			continue
245		}
246		c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address
247		if err != nil {
248			t.Fatal(err)
249		}
250		defer c.Close()
251		r, err := ipv4.NewRawConn(c)
252		if err != nil {
253			t.Fatal(err)
254		}
255		if err := r.JoinGroup(&ifi, &gaddr); err != nil {
256			t.Fatal(err)
257		}
258		mlt = append(mlt, &ml{r, &ift[i]})
259	}
260	for _, m := range mlt {
261		if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil {
262			t.Fatal(err)
263		}
264	}
265}
266