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 }