code review 4066044: net: Add IPv4 Multicast support to UDPConn (issue4066044)

107 views
Skip to first unread message

da...@cheney.net

unread,
Jan 27, 2011, 7:21:16 AM1/27/11
to a...@golang.org, r...@golang.org, golan...@googlegroups.com, re...@codereview.appspotmail.com
Reviewers: adg, rsc,

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))
+}


Russ Cox

unread,
Jan 27, 2011, 8:16:36 AM1/27/11
to da...@cheney.net, a...@golang.org, r...@golang.org, golan...@googlegroups.com, re...@codereview.appspotmail.com
> Support for multicast differs between darwin, linux and freebsd.

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

da...@cheney.net

unread,
Jan 27, 2011, 4:40:20 PM1/27/11
to a...@golang.org, r...@golang.org, golan...@googlegroups.com, re...@codereview.appspotmail.com
Hello adg, rsc (cc: golan...@googlegroups.com),

Please take another look.


http://codereview.appspot.com/4066044/

Dave Cheney

unread,
Jan 27, 2011, 4:29:49 PM1/27/11
to r...@golang.org, a...@golang.org, golan...@googlegroups.com, re...@codereview.appspotmail.com
Hi Russ,

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

Reply all
Reply to author
Forward
0 new messages