mirror of
https://github.com/golang/go
synced 2024-11-22 01:44:40 -07:00
net: ListenMulticastUDP to listen concurrently across multiple listeners
This CL introduces new function ListenMulticastUDP to fix multicast UDP listening across multiple listeners issue, to replace old multicast methods JoinGroup and LeaveGroup on UDPConn. This CL also enables multicast testing by default. Fixes #2730. R=rsc, paul.a.lalonde, fullung, devon.odell CC=golang-dev https://golang.org/cl/5562048
This commit is contained in:
parent
c86e03975c
commit
2f63afdc7a
@ -1215,8 +1215,13 @@ reads and writes will time out and no longer block.
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
There is also a new <a href="/pkg/net/#DialTimeout"><code>net.DialTimeout</code></a> method to simplify
|
There are also new functions
|
||||||
timing out dialing a network address.
|
<a href="/pkg/net/#DialTimeout"><code>net.DialTimeout</code></a>
|
||||||
|
to simplify timing out dialing a network address and
|
||||||
|
<a href="/pkg/net/#ListenMulticastUDP"><code>net.ListenMulticastUDP</code></a>
|
||||||
|
to allow multicast UDP to listen concurrently across multiple listeners.
|
||||||
|
The <code>net.ListenMulticastUDP</code> function replaces the old
|
||||||
|
<code>JoinGroup</code> and <code>LeaveGroup</code> methods.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -1118,8 +1118,13 @@ reads and writes will time out and no longer block.
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
There is also a new <a href="/pkg/net/#DialTimeout"><code>net.DialTimeout</code></a> method to simplify
|
There are also new functions
|
||||||
timing out dialing a network address.
|
<a href="/pkg/net/#DialTimeout"><code>net.DialTimeout</code></a>
|
||||||
|
to simplify timing out dialing a network address and
|
||||||
|
<a href="/pkg/net/#ListenMulticastUDP"><code>net.ListenMulticastUDP</code></a>
|
||||||
|
to allow multicast UDP to listen concurrently across multiple listeners.
|
||||||
|
The <code>net.ListenMulticastUDP</code> function replaces the old
|
||||||
|
<code>JoinGroup</code> and <code>LeaveGroup</code> methods.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -5,43 +5,36 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var multicast = flag.Bool("multicast", false, "enable multicast tests")
|
var listenMulticastUDPTests = []struct {
|
||||||
|
|
||||||
var multicastUDPTests = []struct {
|
|
||||||
net string
|
net string
|
||||||
laddr IP
|
gaddr *UDPAddr
|
||||||
gaddr IP
|
|
||||||
flags Flags
|
flags Flags
|
||||||
ipv6 bool
|
ipv6 bool
|
||||||
}{
|
}{
|
||||||
// cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
|
// cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
|
||||||
{"udp", IPv4zero, IPv4(224, 0, 0, 254), (FlagUp | FlagLoopback), false},
|
{"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
|
||||||
{"udp4", IPv4zero, IPv4(224, 0, 0, 254), (FlagUp | FlagLoopback), false},
|
{"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
|
||||||
{"udp", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
|
{"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
{"udp6", IPv6unspecified, ParseIP("ff01::114"), (FlagUp | FlagLoopback), true},
|
{"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
{"udp6", IPv6unspecified, ParseIP("ff02::114"), (FlagUp | FlagLoopback), true},
|
{"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
{"udp6", IPv6unspecified, ParseIP("ff04::114"), (FlagUp | FlagLoopback), true},
|
{"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
{"udp6", IPv6unspecified, ParseIP("ff05::114"), (FlagUp | FlagLoopback), true},
|
{"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
{"udp6", IPv6unspecified, ParseIP("ff08::114"), (FlagUp | FlagLoopback), true},
|
{"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
{"udp6", IPv6unspecified, ParseIP("ff0e::114"), (FlagUp | FlagLoopback), true},
|
{"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMulticastUDP(t *testing.T) {
|
func TestListenMulticastUDP(t *testing.T) {
|
||||||
if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
|
switch runtime.GOOS {
|
||||||
return
|
case "netbsd", "openbsd", "plan9", "windows":
|
||||||
}
|
|
||||||
if !*multicast {
|
|
||||||
t.Logf("test disabled; use --multicast to enable")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range multicastUDPTests {
|
for _, tt := range listenMulticastUDPTests {
|
||||||
if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
|
if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -60,14 +53,11 @@ func TestMulticastUDP(t *testing.T) {
|
|||||||
t.Logf("an appropriate multicast interface not found")
|
t.Logf("an appropriate multicast interface not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c, err := ListenUDP(tt.net, &UDPAddr{IP: tt.laddr})
|
c, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ListenUDP failed: %v", err)
|
t.Fatalf("ListenMulticastUDP failed: %v", err)
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
if err := c.JoinGroup(ifi, tt.gaddr); err != nil {
|
|
||||||
t.Fatalf("JoinGroup failed: %v", err)
|
|
||||||
}
|
}
|
||||||
|
defer c.Close() // test to listen concurrently across multiple listeners
|
||||||
if !tt.ipv6 {
|
if !tt.ipv6 {
|
||||||
testIPv4MulticastSocketOptions(t, c.fd, ifi)
|
testIPv4MulticastSocketOptions(t, c.fd, ifi)
|
||||||
} else {
|
} else {
|
||||||
@ -79,7 +69,7 @@ func TestMulticastUDP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var found bool
|
var found bool
|
||||||
for _, ifma := range ifmat {
|
for _, ifma := range ifmat {
|
||||||
if ifma.(*IPAddr).IP.Equal(tt.gaddr) {
|
if ifma.(*IPAddr).IP.Equal(tt.gaddr.IP) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -87,23 +77,16 @@ func TestMulticastUDP(t *testing.T) {
|
|||||||
if !found {
|
if !found {
|
||||||
t.Fatalf("%q not found in RIB", tt.gaddr.String())
|
t.Fatalf("%q not found in RIB", tt.gaddr.String())
|
||||||
}
|
}
|
||||||
if err := c.LeaveGroup(ifi, tt.gaddr); err != nil {
|
|
||||||
t.Fatalf("LeaveGroup failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimpleMulticastUDP(t *testing.T) {
|
func TestSimpleListenMulticastUDP(t *testing.T) {
|
||||||
if runtime.GOOS == "plan9" {
|
switch runtime.GOOS {
|
||||||
return
|
case "plan9":
|
||||||
}
|
|
||||||
if !*multicast {
|
|
||||||
t.Logf("test disabled; use --multicast to enable")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range multicastUDPTests {
|
for _, tt := range listenMulticastUDPTests {
|
||||||
var ifi *Interface
|
|
||||||
if tt.ipv6 {
|
if tt.ipv6 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -112,6 +95,7 @@ func TestSimpleMulticastUDP(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Interfaces failed: %v", err)
|
t.Fatalf("Interfaces failed: %v", err)
|
||||||
}
|
}
|
||||||
|
var ifi *Interface
|
||||||
for _, x := range ift {
|
for _, x := range ift {
|
||||||
if x.Flags&tt.flags == tt.flags {
|
if x.Flags&tt.flags == tt.flags {
|
||||||
ifi = &x
|
ifi = &x
|
||||||
@ -122,17 +106,11 @@ func TestSimpleMulticastUDP(t *testing.T) {
|
|||||||
t.Logf("an appropriate multicast interface not found")
|
t.Logf("an appropriate multicast interface not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c, err := ListenUDP(tt.net, &UDPAddr{IP: tt.laddr})
|
c, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ListenUDP failed: %v", err)
|
t.Fatalf("ListenMulticastUDP failed: %v", err)
|
||||||
}
|
|
||||||
defer c.Close()
|
|
||||||
if err := c.JoinGroup(ifi, tt.gaddr); err != nil {
|
|
||||||
t.Fatalf("JoinGroup failed: %v", err)
|
|
||||||
}
|
|
||||||
if err := c.LeaveGroup(ifi, tt.gaddr); err != nil {
|
|
||||||
t.Fatalf("LeaveGroup failed: %v", err)
|
|
||||||
}
|
}
|
||||||
|
c.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,18 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal
|
|||||||
syscall.CloseOnExec(s)
|
syscall.CloseOnExec(s)
|
||||||
syscall.ForkLock.RUnlock()
|
syscall.ForkLock.RUnlock()
|
||||||
|
|
||||||
setDefaultSockopts(s, f, t)
|
err = setDefaultSockopts(s, f, t)
|
||||||
|
if err != nil {
|
||||||
|
closesocket(s)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if la != nil {
|
if la != nil {
|
||||||
|
la, err = listenerSockaddr(s, f, la, toAddr)
|
||||||
|
if err != nil {
|
||||||
|
closesocket(s)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
err = syscall.Bind(s, la)
|
err = syscall.Bind(s, la)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closesocket(s)
|
closesocket(s)
|
||||||
|
@ -31,3 +31,27 @@ func maxListenerBacklog() int {
|
|||||||
}
|
}
|
||||||
return int(n)
|
return int(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
|
||||||
|
a := toAddr(la)
|
||||||
|
if a == nil {
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *UDPAddr:
|
||||||
|
if v.IP.IsMulticast() {
|
||||||
|
err := setDefaultMulticastSockopts(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch f {
|
||||||
|
case syscall.AF_INET:
|
||||||
|
v.IP = IPv4zero
|
||||||
|
case syscall.AF_INET6:
|
||||||
|
v.IP = IPv6unspecified
|
||||||
|
}
|
||||||
|
return v.sockaddr(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
@ -25,3 +25,27 @@ func maxListenerBacklog() int {
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
|
||||||
|
a := toAddr(la)
|
||||||
|
if a == nil {
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *UDPAddr:
|
||||||
|
if v.IP.IsMulticast() {
|
||||||
|
err := setDefaultMulticastSockopts(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch f {
|
||||||
|
case syscall.AF_INET:
|
||||||
|
v.IP = IPv4zero
|
||||||
|
case syscall.AF_INET6:
|
||||||
|
v.IP = IPv6unspecified
|
||||||
|
}
|
||||||
|
return v.sockaddr(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
@ -12,3 +12,27 @@ func maxListenerBacklog() int {
|
|||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
return syscall.SOMAXCONN
|
return syscall.SOMAXCONN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) {
|
||||||
|
a := toAddr(la)
|
||||||
|
if a == nil {
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *UDPAddr:
|
||||||
|
if v.IP.IsMulticast() {
|
||||||
|
err := setDefaultMulticastSockopts(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch f {
|
||||||
|
case syscall.AF_INET:
|
||||||
|
v.IP = IPv4zero
|
||||||
|
case syscall.AF_INET6:
|
||||||
|
v.IP = IPv6unspecified
|
||||||
|
}
|
||||||
|
return v.sockaddr(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
@ -9,37 +9,55 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setDefaultSockopts(s, f, t int) {
|
func setDefaultSockopts(s, f, t int) error {
|
||||||
switch f {
|
switch f {
|
||||||
case syscall.AF_INET6:
|
case syscall.AF_INET6:
|
||||||
// Allow both IP versions even if the OS default is otherwise.
|
// Allow both IP versions even if the OS default is otherwise.
|
||||||
|
// Note that some operating systems never admit this option.
|
||||||
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
|
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f == syscall.AF_UNIX ||
|
if f == syscall.AF_UNIX ||
|
||||||
(f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM {
|
(f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM {
|
||||||
// Allow reuse of recently-used addresses.
|
// Allow reuse of recently-used addresses.
|
||||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Allow reuse of recently-used ports.
|
// Allow reuse of recently-used ports.
|
||||||
// This option is supported only in descendants of 4.4BSD,
|
// This option is supported only in descendants of 4.4BSD,
|
||||||
// to make an effective multicast application and an application
|
// to make an effective multicast application and an application
|
||||||
// that requires quick draw possible.
|
// that requires quick draw possible.
|
||||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
|
err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow broadcast.
|
// Allow broadcast.
|
||||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDefaultMulticastSockopts(fd *netFD) {
|
func setDefaultMulticastSockopts(s int) error {
|
||||||
fd.incref()
|
|
||||||
defer fd.decref()
|
|
||||||
// Allow multicast UDP and raw IP datagram sockets to listen
|
// Allow multicast UDP and raw IP datagram sockets to listen
|
||||||
// concurrently across multiple listeners.
|
// concurrently across multiple listeners.
|
||||||
syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -7,31 +7,43 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setDefaultSockopts(s, f, t int) {
|
func setDefaultSockopts(s, f, t int) error {
|
||||||
switch f {
|
switch f {
|
||||||
case syscall.AF_INET6:
|
case syscall.AF_INET6:
|
||||||
// Allow both IP versions even if the OS default is otherwise.
|
// Allow both IP versions even if the OS default is otherwise.
|
||||||
|
// Note that some operating systems never admit this option.
|
||||||
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
|
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f == syscall.AF_UNIX ||
|
if f == syscall.AF_UNIX ||
|
||||||
(f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM {
|
(f == syscall.AF_INET || f == syscall.AF_INET6) && t == syscall.SOCK_STREAM {
|
||||||
// Allow reuse of recently-used addresses.
|
// Allow reuse of recently-used addresses.
|
||||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow broadcast.
|
// Allow broadcast.
|
||||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDefaultMulticastSockopts(fd *netFD) {
|
func setDefaultMulticastSockopts(s int) error {
|
||||||
fd.incref()
|
|
||||||
defer fd.decref()
|
|
||||||
// Allow multicast UDP and raw IP datagram sockets to listen
|
// Allow multicast UDP and raw IP datagram sockets to listen
|
||||||
// concurrently across multiple listeners.
|
// concurrently across multiple listeners.
|
||||||
syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,15 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setDefaultSockopts(s syscall.Handle, f, t int) {
|
func setDefaultSockopts(s syscall.Handle, f, t int) error {
|
||||||
switch f {
|
switch f {
|
||||||
case syscall.AF_INET6:
|
case syscall.AF_INET6:
|
||||||
// Allow both IP versions even if the OS default is otherwise.
|
// Allow both IP versions even if the OS default is otherwise.
|
||||||
|
// Note that some operating systems never admit this option.
|
||||||
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
|
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,14 +27,20 @@ func setDefaultSockopts(s syscall.Handle, f, t int) {
|
|||||||
// to be handled by the correct socket.
|
// to be handled by the correct socket.
|
||||||
|
|
||||||
// Allow broadcast.
|
// Allow broadcast.
|
||||||
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDefaultMulticastSockopts(fd *netFD) {
|
func setDefaultMulticastSockopts(s syscall.Handle) error {
|
||||||
fd.incref()
|
|
||||||
defer fd.decref()
|
|
||||||
// Allow multicast UDP and raw IP datagram sockets to listen
|
// Allow multicast UDP and raw IP datagram sockets to listen
|
||||||
// concurrently across multiple listeners.
|
// concurrently across multiple listeners.
|
||||||
syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("setsockopt", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -186,20 +186,10 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
|
|||||||
return &UDPConn{*l.plan9Conn()}, nil
|
return &UDPConn{*l.plan9Conn()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinGroup joins the IP multicast group named by addr on ifi,
|
// ListenMulticastUDP listens for incoming multicast UDP packets
|
||||||
// which specifies the interface to join. JoinGroup uses the
|
// addressed to the group address gaddr on ifi, which specifies
|
||||||
// default multicast interface if ifi is nil.
|
// the interface to join. ListenMulticastUDP uses default
|
||||||
func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) error {
|
// multicast interface if ifi is nil.
|
||||||
if !c.ok() {
|
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
|
||||||
return os.EINVAL
|
return nil, os.EPLAN9
|
||||||
}
|
|
||||||
return os.EPLAN9
|
|
||||||
}
|
|
||||||
|
|
||||||
// LeaveGroup exits the IP multicast group named by addr on ifi.
|
|
||||||
func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) error {
|
|
||||||
if !c.ok() {
|
|
||||||
return os.EINVAL
|
|
||||||
}
|
|
||||||
return os.EPLAN9
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func (c *UDPConn) ok() bool { return c != nil && c.fd != nil }
|
|||||||
// Implementation of the Conn interface - see Conn for documentation.
|
// Implementation of the Conn interface - see Conn for documentation.
|
||||||
|
|
||||||
// Read implements the Conn Read method.
|
// Read implements the Conn Read method.
|
||||||
func (c *UDPConn) Read(b []byte) (n int, err error) {
|
func (c *UDPConn) Read(b []byte) (int, error) {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
return 0, os.EINVAL
|
return 0, os.EINVAL
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ func (c *UDPConn) Read(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write implements the Conn Write method.
|
// Write implements the Conn Write method.
|
||||||
func (c *UDPConn) Write(b []byte) (n int, err error) {
|
func (c *UDPConn) Write(b []byte) (int, error) {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
return 0, os.EINVAL
|
return 0, os.EINVAL
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadFrom implements the PacketConn ReadFrom method.
|
// ReadFrom implements the PacketConn ReadFrom method.
|
||||||
func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
|
func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
|
||||||
if !c.ok() {
|
if !c.ok() {
|
||||||
return 0, nil, os.EINVAL
|
return 0, nil, os.EINVAL
|
||||||
}
|
}
|
||||||
@ -207,6 +207,11 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
|
|||||||
return c.WriteToUDP(b, a)
|
return c.WriteToUDP(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File returns a copy of the underlying os.File, set to blocking mode.
|
||||||
|
// It is the caller's responsibility to close f when finished.
|
||||||
|
// Closing c does not affect f, and closing f does not affect c.
|
||||||
|
func (c *UDPConn) File() (f *os.File, err error) { return c.fd.dup() }
|
||||||
|
|
||||||
// DialUDP connects to the remote address raddr on the network net,
|
// DialUDP connects to the remote address raddr on the network net,
|
||||||
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
|
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
|
||||||
// as the local address for the connection.
|
// as the local address for the connection.
|
||||||
@ -246,36 +251,75 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
|
|||||||
return newUDPConn(fd), nil
|
return newUDPConn(fd), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// File returns a copy of the underlying os.File, set to blocking mode.
|
// ListenMulticastUDP listens for incoming multicast UDP packets
|
||||||
// It is the caller's responsibility to close f when finished.
|
// addressed to the group address gaddr on ifi, which specifies
|
||||||
// Closing c does not affect f, and closing f does not affect c.
|
// the interface to join. ListenMulticastUDP uses default
|
||||||
func (c *UDPConn) File() (f *os.File, err error) { return c.fd.dup() }
|
// multicast interface if ifi is nil.
|
||||||
|
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
|
||||||
// JoinGroup joins the IP multicast group named by addr on ifi,
|
switch net {
|
||||||
// which specifies the interface to join. JoinGroup uses the
|
case "udp", "udp4", "udp6":
|
||||||
// default multicast interface if ifi is nil.
|
default:
|
||||||
func (c *UDPConn) JoinGroup(ifi *Interface, addr IP) error {
|
return nil, UnknownNetworkError(net)
|
||||||
if !c.ok() {
|
|
||||||
return os.EINVAL
|
|
||||||
}
|
}
|
||||||
setDefaultMulticastSockopts(c.fd)
|
if gaddr == nil || gaddr.IP == nil {
|
||||||
ip := addr.To4()
|
return nil, &OpError{"listenmulticastudp", "udp", nil, errMissingAddress}
|
||||||
if ip != nil {
|
|
||||||
return joinIPv4GroupUDP(c, ifi, ip)
|
|
||||||
}
|
}
|
||||||
return joinIPv6GroupUDP(c, ifi, addr)
|
fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := newUDPConn(fd)
|
||||||
|
ip4 := gaddr.IP.To4()
|
||||||
|
if ip4 != nil {
|
||||||
|
err := listenIPv4MulticastUDP(c, ifi, ip4)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := listenIPv6MulticastUDP(c, ifi, gaddr.IP)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeaveGroup exits the IP multicast group named by addr on ifi.
|
func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
|
||||||
func (c *UDPConn) LeaveGroup(ifi *Interface, addr IP) error {
|
if ifi != nil {
|
||||||
if !c.ok() {
|
err := setIPv4MulticastInterface(c.fd, ifi)
|
||||||
return os.EINVAL
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ip := addr.To4()
|
err := setIPv4MulticastLoopback(c.fd, false)
|
||||||
if ip != nil {
|
if err != nil {
|
||||||
return leaveIPv4GroupUDP(c, ifi, ip)
|
return err
|
||||||
}
|
}
|
||||||
return leaveIPv6GroupUDP(c, ifi, addr)
|
err = joinIPv4GroupUDP(c, ifi, ip)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
|
||||||
|
if ifi != nil {
|
||||||
|
err := setIPv6MulticastInterface(c.fd, ifi)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := setIPv6MulticastLoopback(c.fd, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = joinIPv6GroupUDP(c, ifi, ip)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
|
func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user