golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv4/multicast_test.go (about)

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