go.net/ipv4: restructure ancillary data socket option handling

This CL chops existing ancillary data socket option handlers and
puts them into platform dependent ancillary data socket option
binding table for code readability.

Fixes golang/go#6710.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/153860043
This commit is contained in:
Mikio Hara
2014-10-04 09:51:11 +09:00
parent 353547e1a2
commit edb50c0ae2
12 changed files with 219 additions and 256 deletions

View File

@@ -11,7 +11,7 @@ import (
)
type rawOpt struct {
sync.Mutex
sync.RWMutex
cflags ControlFlags
}
@@ -50,3 +50,21 @@ func (cm *ControlMessage) String() string {
}
return fmt.Sprintf("ttl: %v, src: %v, dst: %v, ifindex: %v", cm.TTL, cm.Src, cm.Dst, cm.IfIndex)
}
// Ancillary data socket options
const (
ctlTTL = iota // header field
ctlSrc // header field
ctlDst // header field
ctlInterface // inbound or outbound interface
ctlPacketInfo // inbound or outbound packet path
ctlMax
)
// A ctlOpt represents a binding for ancillary data socket option.
type ctlOpt struct {
name int // option name, must be equal or greater than 1
length int // option length
marshal func([]byte, *ControlMessage) []byte
parse func(*ControlMessage, []byte)
}

View File

@@ -12,131 +12,27 @@ import (
"unsafe"
)
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.Lock()
defer opt.Unlock()
if cf&FlagTTL != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
if sockOpts[ssoPacketInfo].name > 0 {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := setInt(fd, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
return err
}
if on {
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
}
}
} else {
if cf&FlagDst != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveDst], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagDst)
} else {
opt.clear(FlagDst)
}
}
if cf&FlagInterface != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveInterface], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagInterface)
} else {
opt.clear(FlagInterface)
}
}
}
return nil
func marshalDst(b []byte, cm *ControlMessage) []byte {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVDSTADDR
m.SetLen(syscall.CmsgLen(net.IPv4len))
return b[syscall.CmsgSpace(net.IPv4len):]
}
func (opt *rawOpt) oobLen() (l int) {
if opt.isset(FlagTTL) {
l += syscall.CmsgSpace(1)
}
if sockOpts[ssoPacketInfo].name > 0 {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
} else {
if opt.isset(FlagDst) {
l += syscall.CmsgSpace(net.IPv4len)
}
if opt.isset(FlagInterface) {
l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
}
}
return
func parseDst(cm *ControlMessage, b []byte) {
cm.Dst = b[:net.IPv4len]
}
func (opt *rawOpt) marshalControlMessage() (oob []byte) {
var off int
oob = make([]byte, opt.oobLen())
if opt.isset(FlagTTL) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
if sockOpts[ssoPacketInfo].name > 0 {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = sysIP_PKTINFO
m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
off += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
} else {
if opt.isset(FlagDst) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVDSTADDR
m.SetLen(syscall.CmsgLen(net.IPv4len))
off += syscall.CmsgSpace(net.IPv4len)
}
if opt.isset(FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVIF
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
}
}
return
func marshalInterface(b []byte, cm *ControlMessage) []byte {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVIF
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
return b[syscall.CmsgSpace(syscall.SizeofSockaddrDatalink):]
}
func (cm *ControlMessage) oobLen() (l int) {
if sockOpts[ssoPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex != 0) {
l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) {
switch m.Header.Type {
case sysIP_RECVTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case sysIP_RECVDSTADDR:
cm.Dst = m.Data[:net.IPv4len]
case sysIP_RECVIF:
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(sadl.Index)
case sysIP_PKTINFO:
pi := (*sysInetPktinfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.Ifindex)
cm.Dst = pi.Addr[:]
}
func parseInterface(cm *ControlMessage, b []byte) {
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&b[0]))
cm.IfIndex = int(sadl.Index)
}

View File

@@ -1,84 +0,0 @@
// Copyright 2012 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 ipv4
import (
"syscall"
"unsafe"
)
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.Lock()
defer opt.Unlock()
if cf&FlagTTL != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := setInt(fd, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
return err
}
if on {
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
}
}
return nil
}
func (opt *rawOpt) oobLen() (l int) {
if opt.isset(FlagTTL) {
l += syscall.CmsgSpace(1)
}
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (opt *rawOpt) marshalControlMessage() (oob []byte) {
var off int
oob = make([]byte, opt.oobLen())
if opt.isset(FlagTTL) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[0]))
m.Level = ianaProtocolIP
m.Type = sysIP_PKTINFO
m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
off += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (cm *ControlMessage) oobLen() (l int) {
if cm.Src.To4() != nil || cm.IfIndex != 0 {
l += syscall.CmsgSpace(sysSizeofInetPktinfo)
}
return
}
func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) {
switch m.Header.Type {
case sysIP_TTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case sysIP_PKTINFO:
pi := (*sysInetPktinfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.Ifindex)
cm.Dst = pi.Addr[:]
}
}

View File

@@ -1,11 +0,0 @@
// 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,!linux
package ipv4
func (cm *ControlMessage) marshalPacketInfo() (oob []byte) {
return nil
}

View File

@@ -11,20 +11,25 @@ import (
"unsafe"
)
func (cm *ControlMessage) marshalPacketInfo() (oob []byte) {
if l := cm.oobLen(); l > 0 {
oob = make([]byte, l)
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[0]))
m.Level = ianaProtocolIP
m.Type = sysIP_PKTINFO
m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
pi := (*sysInetPktinfo)(unsafe.Pointer(&oob[syscall.CmsgLen(0)]))
func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
m.Level = ianaProtocolIP
m.Type = sysIP_PKTINFO
m.SetLen(syscall.CmsgLen(sysSizeofInetPktinfo))
if cm != nil {
pi := (*sysInetPktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
if ip := cm.Src.To4(); ip != nil {
copy(pi.Addr[:], ip)
copy(pi.Spec_dst[:], ip)
}
if cm.IfIndex != 0 {
pi.setIfindex(cm.IfIndex)
}
}
return
return b[syscall.CmsgSpace(sysSizeofInetPktinfo):]
}
func parsePacketInfo(cm *ControlMessage, b []byte) {
pi := (*sysInetPktinfo)(unsafe.Pointer(&b[0]))
cm.IfIndex = int(pi.Ifindex)
cm.Dst = pi.Addr[:]
}

View File

@@ -9,12 +9,97 @@ package ipv4
import (
"os"
"syscall"
"unsafe"
)
func newControlMessage(opt *rawOpt) (oob []byte) {
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.Lock()
defer opt.Unlock()
return opt.marshalControlMessage()
if cf&FlagTTL != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveTTL], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
if sockOpts[ssoPacketInfo].name > 0 {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := setInt(fd, &sockOpts[ssoPacketInfo], boolint(on)); err != nil {
return err
}
if on {
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
}
}
} else {
if cf&FlagDst != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveDst], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagDst)
} else {
opt.clear(FlagDst)
}
}
if cf&FlagInterface != 0 {
if err := setInt(fd, &sockOpts[ssoReceiveInterface], boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagInterface)
} else {
opt.clear(FlagInterface)
}
}
}
return nil
}
func newControlMessage(opt *rawOpt) (oob []byte) {
opt.RLock()
var l int
if opt.isset(FlagTTL) {
l += syscall.CmsgSpace(ctlOpts[ctlTTL].length)
}
if ctlOpts[ctlPacketInfo].name > 0 {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
}
} else {
if opt.isset(FlagDst) {
l += syscall.CmsgSpace(ctlOpts[ctlDst].length)
}
if opt.isset(FlagInterface) {
l += syscall.CmsgSpace(ctlOpts[ctlInterface].length)
}
}
if l > 0 {
oob = make([]byte, l)
b := oob
if opt.isset(FlagTTL) {
b = ctlOpts[ctlTTL].marshal(b, nil)
}
if ctlOpts[ctlPacketInfo].name > 0 {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
b = ctlOpts[ctlPacketInfo].marshal(b, nil)
}
} else {
if opt.isset(FlagDst) {
b = ctlOpts[ctlDst].marshal(b, nil)
}
if opt.isset(FlagInterface) {
b = ctlOpts[ctlInterface].marshal(b, nil)
}
}
}
opt.RUnlock()
return
}
func parseControlMessage(b []byte) (*ControlMessage, error) {
@@ -30,7 +115,16 @@ func parseControlMessage(b []byte) (*ControlMessage, error) {
if m.Header.Level != ianaProtocolIP {
continue
}
cm.parseControlMessage(&m)
switch int(m.Header.Type) {
case ctlOpts[ctlTTL].name:
ctlOpts[ctlTTL].parse(cm, m.Data[:])
case ctlOpts[ctlDst].name:
ctlOpts[ctlDst].parse(cm, m.Data[:])
case ctlOpts[ctlInterface].name:
ctlOpts[ctlInterface].parse(cm, m.Data[:])
case ctlOpts[ctlPacketInfo].name:
ctlOpts[ctlPacketInfo].parse(cm, m.Data[:])
}
}
return cm, nil
}
@@ -39,5 +133,32 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm == nil {
return nil
}
return cm.marshalPacketInfo()
var l int
if ctlOpts[ctlPacketInfo].name > 0 {
if cm.Src.To4() != nil || cm.IfIndex != 0 {
l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
}
}
if l > 0 {
oob = make([]byte, l)
b := oob
if ctlOpts[ctlPacketInfo].name > 0 {
if cm.Src.To4() != nil || cm.IfIndex != 0 {
b = ctlOpts[ctlPacketInfo].marshal(b, cm)
}
}
}
return
}
func marshalTTL(b []byte, cm *ControlMessage) []byte {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
m.Level = ianaProtocolIP
m.Type = sysIP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
return b[syscall.CmsgSpace(1):]
}
func parseTTL(cm *ControlMessage, b []byte) {
cm.TTL = int(*(*byte)(unsafe.Pointer(&b[:1][0])))
}

View File

@@ -6,21 +6,20 @@
package ipv4
type sysSockoptLen int32
const (
sysIP_PKTINFO = 0
sysSizeofInetPktinfo = 0xc
import (
"net"
"syscall"
)
type sysInetPktinfo struct {
Ifindex uint32
Spec_dst [4]byte /* in_addr */
Addr [4]byte /* in_addr */
}
type sysSockoptLen int32
var (
ctlOpts = [ctlMax]ctlOpt{
ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
}
sockOpts = [ssoMax]sockOpt{
ssoTOS: {sysIP_TOS, ssoTypeInt},
ssoTTL: {sysIP_TTL, ssoTypeInt},

View File

@@ -4,11 +4,20 @@
package ipv4
import "syscall"
import (
"net"
"syscall"
)
type sysSockoptLen int32
var (
ctlOpts = [ctlMax]ctlOpt{
ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
}
sockOpts = [ssoMax]sockOpt{
ssoTOS: {sysIP_TOS, ssoTypeInt},
ssoTTL: {sysIP_TTL, ssoTypeInt},
@@ -40,6 +49,10 @@ func init() {
// The IP_PKTINFO was introduced in OS X 10.7 (Darwin
// 11.0.0). See http://support.apple.com/kb/HT1633.
if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '1' {
ctlOpts[ctlPacketInfo].name = sysIP_PKTINFO
ctlOpts[ctlPacketInfo].length = sysSizeofInetPktinfo
ctlOpts[ctlPacketInfo].marshal = marshalPacketInfo
ctlOpts[ctlPacketInfo].parse = parsePacketInfo
sockOpts[ssoPacketInfo].name = sysIP_RECVPKTINFO
sockOpts[ssoPacketInfo].typ = ssoTypeInt
sockOpts[ssoMulticastInterface].typ = ssoTypeIPMreqn

View File

@@ -4,23 +4,20 @@
package ipv4
import "syscall"
import (
"net"
"syscall"
)
type sysSockoptLen int32
const (
sysIP_PKTINFO = 0
sysSizeofInetPktinfo = 0xc
)
type sysInetPktinfo struct {
Ifindex uint32
Spec_dst [4]byte /* in_addr */
Addr [4]byte /* in_addr */
}
var (
ctlOpts = [ctlMax]ctlOpt{
ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL},
ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst},
ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface},
}
sockOpts = [ssoMax]sockOpt{
ssoTOS: {sysIP_TOS, ssoTypeInt},
ssoTTL: {sysIP_TTL, ssoTypeInt},

View File

@@ -7,6 +7,11 @@ package ipv4
type sysSockoptLen int32
var (
ctlOpts = [ctlMax]ctlOpt{
ctlTTL: {sysIP_TTL, 1, marshalTTL, parseTTL},
ctlPacketInfo: {sysIP_PKTINFO, sysSizeofInetPktinfo, marshalPacketInfo, parsePacketInfo},
}
sockOpts = [ssoMax]sockOpt{
ssoTOS: {sysIP_TOS, ssoTypeInt},
ssoTTL: {sysIP_TTL, ssoTypeInt},

View File

@@ -9,5 +9,7 @@ package ipv4
type sysSockoptLen int32
var (
ctlOpts = [ctlMax]ctlOpt{}
sockOpts = [ssoMax]sockOpt{}
)

View File

@@ -43,6 +43,8 @@ type sysIPMreqSource struct {
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx
var (
ctlOpts = [ctlMax]ctlOpt{}
sockOpts = [ssoMax]sockOpt{
ssoTOS: {sysIP_TOS, ssoTypeInt},
ssoTTL: {sysIP_TTL, ssoTypeInt},