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	"bytes"
9	"fmt"
10	"net"
11	"os"
12	"runtime"
13	"testing"
14	"time"
15
16	"golang.org/x/net/icmp"
17	"golang.org/x/net/internal/iana"
18	"golang.org/x/net/ipv4"
19	"golang.org/x/net/nettest"
20)
21
22var packetConnReadWriteMulticastUDPTests = []struct {
23	addr     string
24	grp, src *net.IPAddr
25}{
26	{"224.0.0.0:0", &net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727
27
28	{"232.0.1.0:0", &net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
29}
30
31func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
32	switch runtime.GOOS {
33	case "fuchsia", "hurd", "illumos", "js", "nacl", "plan9", "solaris", "windows", "zos":
34		t.Skipf("not supported on %s", runtime.GOOS)
35	}
36	ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
37	if err != nil {
38		t.Skip(err)
39	}
40
41	for _, tt := range packetConnReadWriteMulticastUDPTests {
42		t.Run(fmt.Sprintf("addr=%s/grp=%s/src=%s", tt.addr, tt.grp, tt.src), func(t *testing.T) {
43			c, err := net.ListenPacket("udp4", tt.addr)
44			if err != nil {
45				t.Fatal(err)
46			}
47			p := ipv4.NewPacketConn(c)
48			defer func() {
49				if err := p.Close(); err != nil {
50					t.Error(err)
51				}
52			}()
53
54			grp := *p.LocalAddr().(*net.UDPAddr)
55			grp.IP = tt.grp.IP
56			if tt.src == nil {
57				if err := p.JoinGroup(ifi, &grp); err != nil {
58					t.Fatal(err)
59				}
60				defer func() {
61					if err := p.LeaveGroup(ifi, &grp); err != nil {
62						t.Error(err)
63					}
64				}()
65			} else {
66				if err := p.JoinSourceSpecificGroup(ifi, &grp, tt.src); err != nil {
67					switch runtime.GOOS {
68					case "freebsd", "linux":
69					default: // platforms that don't support IGMPv2/3 fail here
70						t.Skipf("not supported on %s", runtime.GOOS)
71					}
72					t.Fatal(err)
73				}
74				defer func() {
75					if err := p.LeaveSourceSpecificGroup(ifi, &grp, tt.src); err != nil {
76						t.Error(err)
77					}
78				}()
79			}
80			if err := p.SetMulticastInterface(ifi); err != nil {
81				t.Fatal(err)
82			}
83			if _, err := p.MulticastInterface(); err != nil {
84				t.Fatal(err)
85			}
86			if err := p.SetMulticastLoopback(true); err != nil {
87				t.Fatal(err)
88			}
89			if _, err := p.MulticastLoopback(); err != nil {
90				t.Fatal(err)
91			}
92			cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
93			wb := []byte("HELLO-R-U-THERE")
94
95			for i, toggle := range []bool{true, false, true} {
96				if err := p.SetControlMessage(cf, toggle); err != nil {
97					if protocolNotSupported(err) {
98						t.Logf("not supported on %s", runtime.GOOS)
99						continue
100					}
101					t.Fatal(err)
102				}
103				if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
104					t.Fatal(err)
105				}
106				if err := p.SetMulticastTTL(i + 1); err != nil {
107					t.Fatal(err)
108				}
109			}
110			if n, err := p.WriteTo(wb, nil, &grp); err != nil {
111				t.Fatal(err)
112			} else if n != len(wb) {
113				t.Fatalf("got %v; want %v", n, len(wb))
114			}
115			rb := make([]byte, 128)
116			if n, _, _, err := p.ReadFrom(rb); err != nil {
117				t.Fatal(err)
118			} else if !bytes.Equal(rb[:n], wb) {
119				t.Fatalf("got %v; want %v", rb[:n], wb)
120			}
121		})
122	}
123}
124
125var packetConnReadWriteMulticastICMPTests = []struct {
126	grp, src *net.IPAddr
127}{
128	{&net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727
129
130	{&net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
131}
132
133func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
134	switch runtime.GOOS {
135	case "fuchsia", "hurd", "illumos", "js", "nacl", "plan9", "solaris", "windows":
136		t.Skipf("not supported on %s", runtime.GOOS)
137	}
138	if !nettest.SupportsRawSocket() {
139		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
140	}
141	ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
142	// Unable to obtain loopback interface on z/OS, so instead we test on any multicast
143	// capable interface.
144	if runtime.GOOS == "zos" {
145		ifi, err = nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast)
146	}
147	if err != nil {
148		t.Skip(err)
149	}
150
151	for _, tt := range packetConnReadWriteMulticastICMPTests {
152		t.Run(fmt.Sprintf("grp=%s/src=%s", tt.grp, tt.src), func(t *testing.T) {
153			c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
154			if err != nil {
155				t.Fatal(err)
156			}
157			p := ipv4.NewPacketConn(c)
158			defer func() {
159				if err := p.Close(); err != nil {
160					t.Error(err)
161				}
162			}()
163
164			if tt.src == nil {
165				if err := p.JoinGroup(ifi, tt.grp); err != nil {
166					t.Fatal(err)
167				}
168				defer func() {
169					if err := p.LeaveGroup(ifi, tt.grp); err != nil {
170						t.Error(err)
171					}
172				}()
173			} else {
174				if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil {
175					switch runtime.GOOS {
176					case "freebsd", "linux":
177					default: // platforms that don't support IGMPv2/3 fail here
178						t.Skipf("not supported on %s", runtime.GOOS)
179					}
180					t.Fatal(err)
181				}
182				defer func() {
183					if err := p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil {
184						t.Error(err)
185					}
186				}()
187			}
188			if err := p.SetMulticastInterface(ifi); err != nil {
189				t.Fatal(err)
190			}
191			if _, err := p.MulticastInterface(); err != nil {
192				t.Fatal(err)
193			}
194			if err := p.SetMulticastLoopback(true); err != nil {
195				t.Fatal(err)
196			}
197			if _, err := p.MulticastLoopback(); err != nil {
198				t.Fatal(err)
199			}
200			cf := ipv4.FlagDst | ipv4.FlagInterface
201			if runtime.GOOS != "illumos" && runtime.GOOS != "solaris" {
202				// Illumos and Solaris never allow modification of ICMP properties.
203				cf |= ipv4.FlagTTL
204			}
205
206			for i, toggle := range []bool{true, false, true} {
207				wb, err := (&icmp.Message{
208					Type: ipv4.ICMPTypeEcho, Code: 0,
209					Body: &icmp.Echo{
210						ID: os.Getpid() & 0xffff, Seq: i + 1,
211						Data: []byte("HELLO-R-U-THERE"),
212					},
213				}).Marshal(nil)
214				if err != nil {
215					t.Fatal(err)
216				}
217				if err := p.SetControlMessage(cf, toggle); err != nil {
218					if protocolNotSupported(err) {
219						t.Logf("not supported on %s", runtime.GOOS)
220						continue
221					}
222					t.Fatal(err)
223				}
224				if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
225					t.Fatal(err)
226				}
227				if err := p.SetMulticastTTL(i + 1); err != nil {
228					t.Fatal(err)
229				}
230				if n, err := p.WriteTo(wb, nil, tt.grp); err != nil {
231					t.Fatal(err)
232				} else if n != len(wb) {
233					t.Fatalf("got %v; want %v", n, len(wb))
234				}
235				rb := make([]byte, 128)
236				if n, _, _, err := p.ReadFrom(rb); err != nil {
237					t.Fatal(err)
238				} else {
239					m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n])
240					if err != nil {
241						t.Fatal(err)
242					}
243					switch {
244					case m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1
245					case m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0
246					default:
247						t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
248					}
249				}
250			}
251		})
252	}
253}
254
255var rawConnReadWriteMulticastICMPTests = []struct {
256	grp, src *net.IPAddr
257}{
258	{&net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727
259
260	{&net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771
261}
262
263func TestRawConnReadWriteMulticastICMP(t *testing.T) {
264	switch runtime.GOOS {
265	case "fuchsia", "hurd", "illumos", "js", "nacl", "plan9", "solaris", "windows":
266		t.Skipf("not supported on %s", runtime.GOOS)
267	}
268	if testing.Short() {
269		t.Skip("to avoid external network")
270	}
271	if !nettest.SupportsRawSocket() {
272		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
273	}
274	ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
275	if err != nil {
276		t.Skipf("not available on %s", runtime.GOOS)
277	}
278
279	for _, tt := range rawConnReadWriteMulticastICMPTests {
280		c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
281		if err != nil {
282			t.Fatal(err)
283		}
284		defer c.Close()
285
286		r, err := ipv4.NewRawConn(c)
287		if err != nil {
288			t.Fatal(err)
289		}
290		defer r.Close()
291		if tt.src == nil {
292			if err := r.JoinGroup(ifi, tt.grp); err != nil {
293				t.Fatal(err)
294			}
295			defer r.LeaveGroup(ifi, tt.grp)
296		} else {
297			if err := r.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil {
298				switch runtime.GOOS {
299				case "freebsd", "linux":
300				default: // platforms that don't support IGMPv2/3 fail here
301					t.Logf("not supported on %s", runtime.GOOS)
302					continue
303				}
304				t.Fatal(err)
305			}
306			defer r.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src)
307		}
308		if err := r.SetMulticastInterface(ifi); err != nil {
309			t.Fatal(err)
310		}
311		if _, err := r.MulticastInterface(); err != nil {
312			t.Fatal(err)
313		}
314		if err := r.SetMulticastLoopback(true); err != nil {
315			t.Fatal(err)
316		}
317		if _, err := r.MulticastLoopback(); err != nil {
318			t.Fatal(err)
319		}
320		cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
321
322		for i, toggle := range []bool{true, false, true} {
323			wb, err := (&icmp.Message{
324				Type: ipv4.ICMPTypeEcho, Code: 0,
325				Body: &icmp.Echo{
326					ID: os.Getpid() & 0xffff, Seq: i + 1,
327					Data: []byte("HELLO-R-U-THERE"),
328				},
329			}).Marshal(nil)
330			if err != nil {
331				t.Fatal(err)
332			}
333			wh := &ipv4.Header{
334				Version:  ipv4.Version,
335				Len:      ipv4.HeaderLen,
336				TOS:      i + 1,
337				TotalLen: ipv4.HeaderLen + len(wb),
338				Protocol: 1,
339				Dst:      tt.grp.IP,
340			}
341			if err := r.SetControlMessage(cf, toggle); err != nil {
342				if protocolNotSupported(err) {
343					t.Logf("not supported on %s", runtime.GOOS)
344					continue
345				}
346				t.Fatal(err)
347			}
348			if err := r.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
349				t.Fatal(err)
350			}
351			r.SetMulticastTTL(i + 1)
352			if err := r.WriteTo(wh, wb, nil); err != nil {
353				t.Fatal(err)
354			}
355			rb := make([]byte, ipv4.HeaderLen+128)
356			if rh, b, _, err := r.ReadFrom(rb); err != nil {
357				t.Fatal(err)
358			} else {
359				m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
360				if err != nil {
361					t.Fatal(err)
362				}
363				switch {
364				case (rh.Dst.IsLoopback() || rh.Dst.IsLinkLocalUnicast() || rh.Dst.IsGlobalUnicast()) && m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1
365				case rh.Dst.IsMulticast() && m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0
366				default:
367					t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
368				}
369			}
370		}
371	}
372}
373