From 153a6a61520e23dabb758b4a612bff144e5e28eb Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Wed, 12 Nov 2014 12:59:23 +0900 Subject: [PATCH] x/net/ipv4: add sticky source-specific multicast socket options LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/173100043 --- ipv4/sockopt.go | 6 ++++++ ipv4/sockopt_ssmreq_stub.go | 17 +++++++++++++++++ ipv4/sockopt_ssmreq_unix.go | 33 +++++++++++++++++++++++++++++++++ ipv4/sockopt_unix.go | 9 +++++++++ ipv4/sockopt_windows.go | 5 +++++ ipv4/sys_darwin.go | 31 +++++++++++++++++++++++++++++++ ipv4/sys_freebsd.go | 27 +++++++++++++++++++++++++-- ipv4/sys_linux.go | 29 +++++++++++++++++++++++++++-- 8 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 ipv4/sockopt_ssmreq_stub.go create mode 100644 ipv4/sockopt_ssmreq_unix.go diff --git a/ipv4/sockopt.go b/ipv4/sockopt.go index 4f4e5b53..4fdb3645 100644 --- a/ipv4/sockopt.go +++ b/ipv4/sockopt.go @@ -18,6 +18,10 @@ const ( ssoHeaderPrepend // ipv4 header ssoJoinGroup // any-source multicast ssoLeaveGroup // any-source multicast + ssoJoinSourceGroup // source-specific multicast + ssoLeaveSourceGroup // source-specific multicast + ssoBlockSourceGroup // any-source or source-specific multicast + ssoUnblockSourceGroup // any-source or source-specific multicast ssoMax ) @@ -28,6 +32,8 @@ const ( ssoTypeInterface ssoTypeIPMreq ssoTypeIPMreqn + ssoTypeGroupReq + ssoTypeGroupSourceReq ) // A sockOpt represents a binding for sticky socket option. diff --git a/ipv4/sockopt_ssmreq_stub.go b/ipv4/sockopt_ssmreq_stub.go new file mode 100644 index 00000000..85465244 --- /dev/null +++ b/ipv4/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 ipv4 + +import "net" + +func setsockoptGroupReq(fd, name int, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} + +func setsockoptGroupSourceReq(fd, name int, ifi *net.Interface, grp, src net.IP) error { + return errOpNoSupport +} diff --git a/ipv4/sockopt_ssmreq_unix.go b/ipv4/sockopt_ssmreq_unix.go new file mode 100644 index 00000000..08a6eaa6 --- /dev/null +++ b/ipv4/sockopt_ssmreq_unix.go @@ -0,0 +1,33 @@ +// 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 ipv4 + +import ( + "net" + "os" + "unsafe" + + "golang.org/x/net/internal/iana" +) + +func setsockoptGroupReq(fd, name int, 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, iana.ProtocolIP, name, unsafe.Pointer(&gr), sysSizeofGroupReq)) +} + +func setsockoptGroupSourceReq(fd, name int, 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, iana.ProtocolIP, name, unsafe.Pointer(&gsr), sysSizeofGroupSourceReq)) +} diff --git a/ipv4/sockopt_unix.go b/ipv4/sockopt_unix.go index 43421198..2a492f9d 100644 --- a/ipv4/sockopt_unix.go +++ b/ipv4/sockopt_unix.go @@ -88,7 +88,16 @@ func setGroup(fd int, opt *sockOpt, ifi *net.Interface, grp net.IP) error { return setsockoptIPMreq(fd, opt.name, ifi, grp) case ssoTypeIPMreqn: return setsockoptIPMreqn(fd, opt.name, ifi, grp) + case ssoTypeGroupReq: + return setsockoptGroupReq(fd, opt.name, 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.name, ifi, grp, src) +} diff --git a/ipv4/sockopt_windows.go b/ipv4/sockopt_windows.go index 70cace53..b4430214 100644 --- a/ipv4/sockopt_windows.go +++ b/ipv4/sockopt_windows.go @@ -53,3 +53,8 @@ func setGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp net.IP) e } return setsockoptIPMreq(fd, opt.name, ifi, grp) } + +func setSourceGroup(fd syscall.Handle, opt *sockOpt, ifi *net.Interface, grp, src net.IP) error { + // TODO(mikio): implement this + return errOpNoSupport +} diff --git a/ipv4/sys_darwin.go b/ipv4/sys_darwin.go index 75f1e512..9f0e8092 100644 --- a/ipv4/sys_darwin.go +++ b/ipv4/sys_darwin.go @@ -7,6 +7,7 @@ package ipv4 import ( "net" "syscall" + "unsafe" ) type sysSockoptLen int32 @@ -56,9 +57,39 @@ func init() { sockOpts[ssoPacketInfo].name = sysIP_RECVPKTINFO sockOpts[ssoPacketInfo].typ = ssoTypeInt sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn + sockOpts[ssoJoinGroup].name = sysMCAST_JOIN_GROUP + sockOpts[ssoJoinGroup].typ = ssoTypeGroupReq + sockOpts[ssoLeaveGroup].name = sysMCAST_LEAVE_GROUP + sockOpts[ssoLeaveGroup].typ = ssoTypeGroupReq + sockOpts[ssoJoinSourceGroup].name = sysMCAST_JOIN_SOURCE_GROUP + sockOpts[ssoJoinSourceGroup].typ = ssoTypeGroupSourceReq + sockOpts[ssoLeaveSourceGroup].name = sysMCAST_LEAVE_SOURCE_GROUP + sockOpts[ssoLeaveSourceGroup].typ = ssoTypeGroupSourceReq + sockOpts[ssoBlockSourceGroup].name = sysMCAST_BLOCK_SOURCE + sockOpts[ssoBlockSourceGroup].typ = ssoTypeGroupSourceReq + sockOpts[ssoUnblockSourceGroup].name = sysMCAST_UNBLOCK_SOURCE + sockOpts[ssoUnblockSourceGroup].typ = ssoTypeGroupSourceReq } } func (pi *sysInetPktinfo) setIfindex(i int) { pi.Ifindex = uint32(i) } + +func (gr *sysGroupReq) setGroup(grp net.IP) { + sa := (*sysSockaddrInet)(unsafe.Pointer(&gr.Pad_cgo_0[0])) + sa.Len = sysSizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sysSockaddrInet)(unsafe.Pointer(&gsr.Pad_cgo_0[0])) + sa.Len = sysSizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sysSockaddrInet)(unsafe.Pointer(&gsr.Pad_cgo_1[0])) + sa.Len = sysSizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +} diff --git a/ipv4/sys_freebsd.go b/ipv4/sys_freebsd.go index 61dcc141..7df658c5 100644 --- a/ipv4/sys_freebsd.go +++ b/ipv4/sys_freebsd.go @@ -7,6 +7,7 @@ package ipv4 import ( "net" "syscall" + "unsafe" ) type sysSockoptLen int32 @@ -28,8 +29,12 @@ var ( ssoReceiveDst: {sysIP_RECVDSTADDR, ssoTypeInt}, ssoReceiveInterface: {sysIP_RECVIF, ssoTypeInt}, ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt}, - ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreq}, - ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreq}, + ssoJoinGroup: {sysMCAST_JOIN_GROUP, ssoTypeGroupReq}, + ssoLeaveGroup: {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq}, + ssoJoinSourceGroup: {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq}, } ) @@ -39,3 +44,21 @@ func init() { sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn } } + +func (gr *sysGroupReq) setGroup(grp net.IP) { + sa := (*sysSockaddrInet)(unsafe.Pointer(&gr.Group)) + sa.Len = sysSizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sysSockaddrInet)(unsafe.Pointer(&gsr.Group)) + sa.Len = sysSizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sysSockaddrInet)(unsafe.Pointer(&gsr.Source)) + sa.Len = sysSizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +} diff --git a/ipv4/sys_linux.go b/ipv4/sys_linux.go index f9dd72f9..ab982155 100644 --- a/ipv4/sys_linux.go +++ b/ipv4/sys_linux.go @@ -4,6 +4,12 @@ package ipv4 +import ( + "net" + "syscall" + "unsafe" +) + type sysSockoptLen int32 var ( @@ -21,11 +27,30 @@ var ( ssoReceiveTTL: {sysIP_RECVTTL, ssoTypeInt}, ssoPacketInfo: {sysIP_PKTINFO, ssoTypeInt}, ssoHeaderPrepend: {sysIP_HDRINCL, ssoTypeInt}, - ssoJoinGroup: {sysIP_ADD_MEMBERSHIP, ssoTypeIPMreqn}, - ssoLeaveGroup: {sysIP_DROP_MEMBERSHIP, ssoTypeIPMreqn}, + ssoJoinGroup: {sysMCAST_JOIN_GROUP, ssoTypeGroupReq}, + ssoLeaveGroup: {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq}, + ssoJoinSourceGroup: {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq}, } ) func (pi *sysInetPktinfo) setIfindex(i int) { pi.Ifindex = int32(i) } + +func (gr *sysGroupReq) setGroup(grp net.IP) { + sa := (*sysSockaddrInet)(unsafe.Pointer(&gr.Group)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sysSockaddrInet)(unsafe.Pointer(&gsr.Group)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sysSockaddrInet)(unsafe.Pointer(&gsr.Source)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +}