Browse Source

x/net/ipv6: add sticky source-specific multicast socket options

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/175010043
Mikio Hara 11 years ago
parent
commit
e9210114b2

+ 6 - 0
ipv6/sockopt.go

@@ -20,6 +20,10 @@ const (
 	ssoICMPFilter                 // icmp filter, RFC 2292 or 3542
 	ssoJoinGroup                  // any-source multicast, RFC 3493
 	ssoLeaveGroup                 // any-source multicast, RFC 3493
+	ssoJoinSourceGroup            // source-specific multicast
+	ssoLeaveSourceGroup           // source-specific multicast
+	ssoBlockSourceGroup           // any-source or source-specific multicast
+	ssoUnblockSourceGroup         // any-source or source-specific multicast
 	ssoMax
 )
 
@@ -30,6 +34,8 @@ const (
 	ssoTypeICMPFilter
 	ssoTypeMTUInfo
 	ssoTypeIPMreq
+	ssoTypeGroupReq
+	ssoTypeGroupSourceReq
 )
 
 // A sockOpt represents a binding for sticky socket option.

+ 17 - 0
ipv6/sockopt_ssmreq_stub.go

@@ -0,0 +1,17 @@
+// Copyright 2014 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.
+
+// +build !darwin,!freebsd,!linux
+
+package ipv6
+
+import "net"
+
+func setsockoptGroupReq(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+	return errOpNoSupport
+}
+
+func setsockoptGroupSourceReq(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+	return errOpNoSupport
+}

+ 31 - 0
ipv6/sockopt_ssmreq_unix.go

@@ -0,0 +1,31 @@
+// Copyright 2014 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.
+
+// +build darwin freebsd linux
+
+package ipv6
+
+import (
+	"net"
+	"os"
+	"unsafe"
+)
+
+func setsockoptGroupReq(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
+	var gr sysGroupReq
+	if ifi != nil {
+		gr.Interface = uint32(ifi.Index)
+	}
+	gr.setGroup(grp)
+	return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, unsafe.Pointer(&gr), sysSizeofGroupReq))
+}
+
+func setsockoptGroupSourceReq(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+	var gsr sysGroupSourceReq
+	if ifi != nil {
+		gsr.Interface = uint32(ifi.Index)
+	}
+	gsr.setSourceGroup(grp, src)
+	return os.NewSyscallError("setsockopt", setsockopt(fd, opt.level, opt.name, unsafe.Pointer(&gsr), sysSizeofGroupSourceReq))
+}

+ 16 - 2
ipv6/sockopt_unix.go

@@ -101,8 +101,22 @@ func getMTUInfo(fd int, opt *sockOpt) (*net.Interface, int, error) {
 }
 
 func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error {
-	if opt.name < 1 || opt.typ != ssoTypeIPMreq {
+	if opt.name < 1 {
 		return errOpNoSupport
 	}
-	return setsockoptIPMreq(fd, opt, ifi, grp)
+	switch opt.typ {
+	case ssoTypeIPMreq:
+		return setsockoptIPMreq(fd, opt, ifi, grp)
+	case ssoTypeGroupReq:
+		return setsockoptGroupReq(fd, opt, ifi, grp)
+	default:
+		return errOpNoSupport
+	}
+}
+
+func setSourceGroup(fd int, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+	if opt.name < 1 || opt.typ != ssoTypeGroupSourceReq {
+		return errOpNoSupport
+	}
+	return setsockoptGroupSourceReq(fd, opt, ifi, grp, src)
 }

+ 5 - 0
ipv6/sockopt_windows.go

@@ -79,3 +79,8 @@ func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) e
 	}
 	return setsockoptIPMreq(fd, opt, ifi, grp)
 }
+
+func setSourceGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error {
+	// TODO(mikio): implement this
+	return errOpNoSupport
+}

+ 1 - 1
ipv6/sys_bsd.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build dragonfly freebsd netbsd openbsd
+// +build dragonfly netbsd openbsd
 
 package ipv6
 

+ 39 - 3
ipv6/sys_darwin.go

@@ -7,6 +7,7 @@ package ipv6
 import (
 	"net"
 	"syscall"
+	"unsafe"
 
 	"golang.org/x/net/internal/iana"
 )
@@ -47,10 +48,11 @@ func init() {
 			continue
 		}
 	}
-	// The IPV6_RECVPATHMTU and IPV6_PATHMTU options were
-	// introduced in OS X 10.7 (Darwin 11.0.0).
+	// The IP_PKTINFO and protocol-independent multicast API were
+	// introduced in OS X 10.7 (Darwin 11.0.0). But it looks like
+	// those features require OS X 10.8 (Darwin 12.0.0) and above.
 	// See http://support.apple.com/kb/HT1633.
-	if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '1' {
+	if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '2' {
 		ctlOpts[ctlTrafficClass].name = sysIPV6_TCLASS
 		ctlOpts[ctlTrafficClass].length = 4
 		ctlOpts[ctlTrafficClass].marshal = marshalTrafficClass
@@ -70,6 +72,22 @@ func init() {
 		sockOpts[ssoPathMTU].level = iana.ProtocolIPv6
 		sockOpts[ssoPathMTU].name = sysIPV6_PATHMTU
 		sockOpts[ssoPathMTU].typ = ssoTypeMTUInfo
+		sockOpts[ssoJoinGroup].name = sysMCAST_JOIN_GROUP
+		sockOpts[ssoJoinGroup].typ = ssoTypeGroupReq
+		sockOpts[ssoLeaveGroup].name = sysMCAST_LEAVE_GROUP
+		sockOpts[ssoLeaveGroup].typ = ssoTypeGroupReq
+		sockOpts[ssoJoinSourceGroup].level = iana.ProtocolIPv6
+		sockOpts[ssoJoinSourceGroup].name = sysMCAST_JOIN_SOURCE_GROUP
+		sockOpts[ssoJoinSourceGroup].typ = ssoTypeGroupSourceReq
+		sockOpts[ssoLeaveSourceGroup].level = iana.ProtocolIPv6
+		sockOpts[ssoLeaveSourceGroup].name = sysMCAST_LEAVE_SOURCE_GROUP
+		sockOpts[ssoLeaveSourceGroup].typ = ssoTypeGroupSourceReq
+		sockOpts[ssoBlockSourceGroup].level = iana.ProtocolIPv6
+		sockOpts[ssoBlockSourceGroup].name = sysMCAST_BLOCK_SOURCE
+		sockOpts[ssoBlockSourceGroup].typ = ssoTypeGroupSourceReq
+		sockOpts[ssoUnblockSourceGroup].level = iana.ProtocolIPv6
+		sockOpts[ssoUnblockSourceGroup].name = sysMCAST_UNBLOCK_SOURCE
+		sockOpts[ssoUnblockSourceGroup].typ = ssoTypeGroupSourceReq
 	}
 }
 
@@ -87,3 +105,21 @@ func (pi *sysInet6Pktinfo) setIfindex(i int) {
 func (mreq *sysIPv6Mreq) setIfindex(i int) {
 	mreq.Interface = uint32(i)
 }
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+	sa := (*sysSockaddrInet6)(unsafe.Pointer(&gr.Pad_cgo_0[0]))
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+	sa := (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Pad_cgo_0[0]))
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], grp)
+	sa = (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Pad_cgo_1[0]))
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], src)
+}

+ 78 - 0
ipv6/sys_freebsd.go

@@ -0,0 +1,78 @@
+// Copyright 2013 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 ipv6
+
+import (
+	"net"
+	"syscall"
+	"unsafe"
+
+	"golang.org/x/net/internal/iana"
+)
+
+type sysSockoptLen int32
+
+var (
+	ctlOpts = [ctlMax]ctlOpt{
+		ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass},
+		ctlHopLimit:     {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit},
+		ctlPacketInfo:   {sysIPV6_PKTINFO, sysSizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo},
+		ctlNextHop:      {sysIPV6_NEXTHOP, sysSizeofSockaddrInet6, marshalNextHop, parseNextHop},
+	}
+
+	sockOpts = [ssoMax]sockOpt{
+		ssoTrafficClass:        {iana.ProtocolIPv6, sysIPV6_TCLASS, ssoTypeInt},
+		ssoHopLimit:            {iana.ProtocolIPv6, sysIPV6_UNICAST_HOPS, ssoTypeInt},
+		ssoMulticastInterface:  {iana.ProtocolIPv6, sysIPV6_MULTICAST_IF, ssoTypeInterface},
+		ssoMulticastHopLimit:   {iana.ProtocolIPv6, sysIPV6_MULTICAST_HOPS, ssoTypeInt},
+		ssoMulticastLoopback:   {iana.ProtocolIPv6, sysIPV6_MULTICAST_LOOP, ssoTypeInt},
+		ssoReceiveTrafficClass: {iana.ProtocolIPv6, sysIPV6_RECVTCLASS, ssoTypeInt},
+		ssoReceiveHopLimit:     {iana.ProtocolIPv6, sysIPV6_RECVHOPLIMIT, ssoTypeInt},
+		ssoReceivePacketInfo:   {iana.ProtocolIPv6, sysIPV6_RECVPKTINFO, ssoTypeInt},
+		ssoReceivePathMTU:      {iana.ProtocolIPv6, sysIPV6_RECVPATHMTU, ssoTypeInt},
+		ssoPathMTU:             {iana.ProtocolIPv6, sysIPV6_PATHMTU, ssoTypeMTUInfo},
+		ssoChecksum:            {iana.ProtocolIPv6, sysIPV6_CHECKSUM, ssoTypeInt},
+		ssoICMPFilter:          {iana.ProtocolIPv6ICMP, sysICMP6_FILTER, ssoTypeICMPFilter},
+		ssoJoinGroup:           {iana.ProtocolIPv6, sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+		ssoLeaveGroup:          {iana.ProtocolIPv6, sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+		ssoJoinSourceGroup:     {iana.ProtocolIPv6, sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+		ssoLeaveSourceGroup:    {iana.ProtocolIPv6, sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+		ssoBlockSourceGroup:    {iana.ProtocolIPv6, sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+		ssoUnblockSourceGroup:  {iana.ProtocolIPv6, sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
+	}
+)
+
+func (sa *sysSockaddrInet6) setSockaddr(ip net.IP, i int) {
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], ip)
+	sa.Scope_id = uint32(i)
+}
+
+func (pi *sysInet6Pktinfo) setIfindex(i int) {
+	pi.Ifindex = uint32(i)
+}
+
+func (mreq *sysIPv6Mreq) setIfindex(i int) {
+	mreq.Interface = uint32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+	sa := (*sysSockaddrInet6)(unsafe.Pointer(&gr.Group))
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+	sa := (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Group))
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], grp)
+	sa = (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Source))
+	sa.Len = sysSizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], src)
+}

+ 22 - 2
ipv6/sys_linux.go

@@ -7,6 +7,7 @@ package ipv6
 import (
 	"net"
 	"syscall"
+	"unsafe"
 
 	"golang.org/x/net/internal/iana"
 )
@@ -33,8 +34,12 @@ var (
 		ssoPathMTU:             {iana.ProtocolIPv6, sysIPV6_PATHMTU, ssoTypeMTUInfo},
 		ssoChecksum:            {iana.ProtocolReserved, sysIPV6_CHECKSUM, ssoTypeInt},
 		ssoICMPFilter:          {iana.ProtocolIPv6ICMP, sysICMPV6_FILTER, ssoTypeICMPFilter},
-		ssoJoinGroup:           {iana.ProtocolIPv6, sysIPV6_ADD_MEMBERSHIP, ssoTypeIPMreq},
-		ssoLeaveGroup:          {iana.ProtocolIPv6, sysIPV6_DROP_MEMBERSHIP, ssoTypeIPMreq},
+		ssoJoinGroup:           {iana.ProtocolIPv6, sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+		ssoLeaveGroup:          {iana.ProtocolIPv6, sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+		ssoJoinSourceGroup:     {iana.ProtocolIPv6, sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+		ssoLeaveSourceGroup:    {iana.ProtocolIPv6, sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+		ssoBlockSourceGroup:    {iana.ProtocolIPv6, sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+		ssoUnblockSourceGroup:  {iana.ProtocolIPv6, sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
 	}
 )
 
@@ -51,3 +56,18 @@ func (pi *sysInet6Pktinfo) setIfindex(i int) {
 func (mreq *sysIPv6Mreq) setIfindex(i int) {
 	mreq.Ifindex = int32(i)
 }
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+	sa := (*sysSockaddrInet6)(unsafe.Pointer(&gr.Group))
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+	sa := (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Group))
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], grp)
+	sa = (*sysSockaddrInet6)(unsafe.Pointer(&gsr.Source))
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], src)
+}