Message:
Hello adg, rsc (cc: golan...@googlegroups.com),
I'd like you to review this change.
Description:
net: Add IPv4 Multicast support to UDPConn
notes:
Support for multicast differs between darwin, linux and freebsd.
The former is very particular about joining a multicast group
if the socket is not pure IPv4, the other OS's are more flexible.
A simple example sets up a socket to listen on the mdns/bonjour
group 224.0.0.251:5353
// ensure the sock is udp4, and the IP is a 4 byte IPv4
socket, err := net.ListenUDP("udp4", &net.UDPAddr {
IP: net.IPv4zero,
// currently darwin will not allow you to bind to
// a port if it is already bound to another process
Port: 5353,
})
if err != nil {
log.Exitf("listen %s", err)
}
defer socket.Close()
err = socket.JoinGroup(net.IPv4(224, 0, 0, 251))
if err != nil {
log.Exitf("join group %s", err)
}
Please review this at http://codereview.appspot.com/4066044/
Affected files:
A src/pkg/net/multicast_test.go
M src/pkg/net/udpsock.go
Index: src/pkg/net/multicast_test.go
===================================================================
new file mode 100644
--- /dev/null
+++ b/src/pkg/net/multicast_test.go
@@ -0,0 +1,56 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "testing"
+)
+
+func TestMulticastJoinAndLeave(t *testing.T) {
+ addr := &UDPAddr{
+ IP: IPv4zero,
+ Port: 0,
+ }
+ // open a UDP Socket
+ socket, err := ListenUDP("udp4", addr)
+ if err != nil {
+ t.Fatalf("Unable to bind to %s: %s", addr, err)
+ }
+ defer socket.Close()
+
+ // try to join group
+ mcast := IPv4(224, 0, 0, 251)
+ err = socket.JoinGroup(mcast)
+ if err != nil {
+ t.Fatalf("Unable to join group %s: %s", addr, err)
+ }
+
+ // try to leave group
+ err = socket.LeaveGroup(mcast)
+ if err != nil {
+ t.Fatalf("Unable to leave group %s: %s", addr, err)
+ }
+}
+
+func TestJoinFailureWithIPv6Address(t *testing.T) {
+ addr := &UDPAddr{
+ IP: IPv4zero,
+ Port: 0,
+ }
+
+ // open a UDP Socket
+ socket, err := ListenUDP("udp4", addr)
+ if err != nil {
+ t.Fatalf("Unable to bind to %s: %s", addr, err)
+ }
+ defer socket.Close()
+ // try to join group
+ mcast := ParseIP("ff02::1")
+ err = socket.JoinGroup(mcast)
+ if err == nil {
+ t.Fatalf("Should fail")
+ }
+ t.Logf("%s", err)
+}
Index: src/pkg/net/udpsock.go
===================================================================
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -279,3 +279,40 @@
// 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 os.Error) { return c.fd.dup() }
+
+// Multicast specific methods.
+
+// JoinGroup binds the UDPConn to a IPv4 Multicast group
+// Only IPv4 addresses are supported
+func (c *UDPConn) JoinGroup(addr IP) os.Error {
+ if !c.ok() {
+ return os.EINVAL
+ }
+ ip := addr.To4()
+ if ip == nil {
+ // not an IPv4 address
+ return os.EINVAL
+ }
+ return os.NewSyscallError("setsockopt",
syscall.SetsockoptString(c.fd.sysfd, syscall.IPPROTO_IP,
syscall.IP_ADD_MEMBERSHIP, makeMReqString(ip)))
+}
+
+// LeaveGroup will cancel an existing IPv4 Multicast group membership
+// Only IPv4 addresses are supported
+func (c *UDPConn) LeaveGroup(addr IP) os.Error {
+ if !c.ok() {
+ return os.EINVAL
+ }
+ ip := addr.To4()
+ if ip == nil {
+ // not an IPv4 address
+ return os.EINVAL
+ }
+ return os.NewSyscallError("setsockopt",
syscall.SetsockoptString(c.fd.sysfd, syscall.IPPROTO_IP,
syscall.IP_DROP_MEMBERSHIP, makeMReqString(ip)))
+}
+
+// construct a string to pass to syscall.SetsocketoptString
+// that confroms to the ip_mreq struct. addr must be an IPv4
+// style address
+func makeMReqString(addr IP) string {
+ return string(append([]byte(addr), 0, 0, 0, 0))
+}
If that's the case then will the uses of package syscall
here work on all 3 systems? If not, then the support routines
should be in fd_$GOOS.go instead of udpsock.go, so
that different code can be used on each.
Russ
Please take another look.
That might have been the wrong turn of phrase. What I mean to say is
darwin is more picky that linux and freebsd.
socket, err := net.ListenUDP("udp", &net.UDPAddr {
IP: net.IPv4zero,
Port: 5356,
})
socket.JoinGroup(net.IPv4(224, 0, 0, 251))
Will work fine under linux and freebsd but throw
2011/01/28 08:15:48 join group setsockopt: invalid argument
under darwin. Setting ListenUDP to "udp4" resolves this issue. I am
thinking that this is due to a difference in the network stack, and
some preference for IPv6 over IPv4. Looking at netstat, when opening
the socket in "udp" mode I get
lucky(~) % netstat -an | grep 5356
udp46 0 0 *.5356 *.*
on linux/386/ubuntu10.10 I get
dave@odessa:~/devel/multicast$ netstat -an | grep 5356
udp6 0 0 :::5356 :::*
yet it will respond to IPv4 multicast datagrams just fine (tested on
port 5353 to listen to mdns traffic)
Opening a "udp4" socket explicitly gives the following on darwin and
linux respectively
lucky(~/devel/multicast) % netstat -an | grep 5356
udp4 0 0 *.5356 *.*
dave@odessa:~/devel/multicast$ netstat -an | grep 5356
udp 0 0 0.0.0.0:5356 0.0.0.0:*
I will reword the comment blocks to emphasise that the socket must be
udp4 and change the wording on the commit to add an explanation.
Cheers
Dave