Skip to content

Commit

Permalink
go.net/ipv4: add support for dragonfly, enable IP_PKTINFO on darwin
Browse files Browse the repository at this point in the history
This CL adds support for dragonfly and IP_PKTINFO support for darwin
with less dependency on syscall package.

Update golang/go#7175
Fixes golang/go#7172.

LGTM=iant
R=golang-codereviews, gobot, iant
CC=golang-codereviews
https://golang.org/cl/97800043
  • Loading branch information
cixtor committed May 19, 2014
1 parent f572974 commit 38dcae4
Show file tree
Hide file tree
Showing 32 changed files with 640 additions and 349 deletions.
141 changes: 81 additions & 60 deletions ipv4/control_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin freebsd netbsd openbsd
// +build darwin dragonfly freebsd netbsd openbsd

package ipv4

import (
"net"
"os"
"syscall"
"unsafe"
)
Expand All @@ -26,96 +25,118 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.clear(FlagTTL)
}
}
if cf&FlagDst != 0 {
if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil {
return err
}
if on {
opt.set(FlagDst)
} else {
opt.clear(FlagDst)
if supportsPacketInfo {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := setIPv4PacketInfo(fd, on); err != nil {
return err
}
if on {
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))

}
}
}
if cf&FlagInterface != 0 {
if err := setIPv4ReceiveInterface(fd, on); err != nil {
return err
} else {
if cf&FlagDst != 0 {
if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil {
return err
}
if on {
opt.set(FlagDst)
} else {
opt.clear(FlagDst)
}
}
if on {
opt.set(FlagInterface)
} else {
opt.clear(FlagInterface)
if cf&FlagInterface != 0 {
if err := setIPv4ReceiveInterface(fd, on); err != nil {
return err
}
if on {
opt.set(FlagInterface)
} else {
opt.clear(FlagInterface)
}
}
}
return nil
}

func newControlMessage(opt *rawOpt) (oob []byte) {
opt.Lock()
defer opt.Unlock()
l, off := 0, 0
func (opt *rawOpt) oobLen() (l int) {
if opt.isset(FlagTTL) {
l += syscall.CmsgSpace(1)
}
if opt.isset(FlagDst) {
l += syscall.CmsgSpace(net.IPv4len)
if supportsPacketInfo {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
} else {
if opt.isset(FlagDst) {
l += syscall.CmsgSpace(net.IPv4len)
}
if opt.isset(FlagInterface) {
l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
}
}
if opt.isset(FlagInterface) {
l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
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 = sysSockoptReceiveTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
if l > 0 {
oob = make([]byte, l)
if opt.isset(FlagTTL) {
if supportsPacketInfo {
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = syscall.IP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
m.Type = sysSockoptPacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
off += syscall.CmsgSpace(sysSizeofPacketInfo)
}
} else {
if opt.isset(FlagDst) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = syscall.IP_RECVDSTADDR
m.Type = sysSockoptReceiveDst
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 = syscall.IP_RECVIF
m.Type = sysSockoptReceiveInterface
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
}
}
return
}

func parseControlMessage(b []byte) (*ControlMessage, error) {
if len(b) == 0 {
return nil, nil
}
cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil {
return nil, os.NewSyscallError("parse socket control message", err)
func (cm *ControlMessage) oobLen() (l int) {
if supportsPacketInfo && (cm.Src.To4() != nil || cm.IfIndex != 0) {
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
cm := &ControlMessage{}
for _, m := range cmsgs {
if m.Header.Level != ianaProtocolIP {
continue
}
switch m.Header.Type {
case syscall.IP_RECVTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IP_RECVDSTADDR:
cm.Dst = m.Data[:net.IPv4len]
case syscall.IP_RECVIF:
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(sadl.Index)
}
}
return cm, nil
return
}

func marshalControlMessage(cm *ControlMessage) []byte {
// TODO(mikio): Implement IP_PKTINFO stuff when OS X 10.8 comes
return nil
func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) {
switch m.Header.Type {
case sysSockoptReceiveTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case sysSockoptReceiveDst:
cm.Dst = m.Data[:net.IPv4len]
case sysSockoptReceiveInterface:
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(sadl.Index)
case sysSockoptPacketInfo:
pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.IfIndex)
cm.Dst = pi.IP[:]
}
}
112 changes: 35 additions & 77 deletions ipv4/control_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@
package ipv4

import (
"os"
"syscall"
"unsafe"
)

// Linux provides a convenient path control option IP_PKTINFO that
// contains IP_SENDSRCADDR, IP_RECVDSTADDR, IP_RECVIF and IP_SENDIF.
const pktinfo = FlagSrc | FlagDst | FlagInterface

func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.Lock()
defer opt.Unlock()
Expand All @@ -27,100 +22,63 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.clear(FlagTTL)
}
}
if cf&pktinfo != 0 {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := setIPv4PacketInfo(fd, on); err != nil {
return err
}
if on {
opt.set(cf & pktinfo)
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & pktinfo)
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
}
}
return nil
}

func newControlMessage(opt *rawOpt) (oob []byte) {
opt.Lock()
defer opt.Unlock()
l, off := 0, 0
func (opt *rawOpt) oobLen() (l int) {
if opt.isset(FlagTTL) {
l += syscall.CmsgSpace(1)
}
if opt.isset(pktinfo) {
l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
}
if l > 0 {
oob = make([]byte, l)
if opt.isset(FlagTTL) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = syscall.IP_RECVTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
if opt.isset(pktinfo) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = syscall.IP_PKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
}
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
return
}

func parseControlMessage(b []byte) (*ControlMessage, error) {
if len(b) == 0 {
return nil, nil
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 = sysSockoptReceiveTTL
m.SetLen(syscall.CmsgLen(1))
off += syscall.CmsgSpace(1)
}
cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil {
return nil, os.NewSyscallError("parse socket control message", err)
if opt.isset(FlagSrc | FlagDst | FlagInterface) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[0]))
m.Level = ianaProtocolIP
m.Type = sysSockoptPacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
off += syscall.CmsgSpace(sysSizeofPacketInfo)
}
cm := &ControlMessage{}
for _, m := range cmsgs {
if m.Header.Level != ianaProtocolIP {
continue
}
switch m.Header.Type {
case syscall.IP_TTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IP_PKTINFO:
pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.Ifindex)
cm.Dst = pi.Addr[:]
}
}
return cm, nil
return
}

func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm == nil {
return
}
l, off := 0, 0
pion := false
func (cm *ControlMessage) oobLen() (l int) {
if cm.Src.To4() != nil || cm.IfIndex != 0 {
pion = true
l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
}
if l > 0 {
oob = make([]byte, l)
if pion {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = syscall.IP_PKTINFO
m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
if ip := cm.Src.To4(); ip != nil {
copy(pi.Addr[:], ip)
}
if cm.IfIndex != 0 {
pi.Ifindex = int32(cm.IfIndex)
}
off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
}
l += syscall.CmsgSpace(sysSizeofPacketInfo)
}
return
}

func (cm *ControlMessage) parseControlMessage(m *syscall.SocketControlMessage) {
switch m.Header.Type {
case sysSockoptTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case sysSockoptPacketInfo:
pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.IfIndex)
cm.Dst = pi.IP[:]
}
}
11 changes: 11 additions & 0 deletions ipv4/control_nonpktinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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
}
30 changes: 30 additions & 0 deletions ipv4/control_pktinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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

import (
"syscall"
"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 = sysSockoptPacketInfo
m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
pi := (*sysPacketInfo)(unsafe.Pointer(&oob[syscall.CmsgLen(0)]))
if ip := cm.Src.To4(); ip != nil {
copy(pi.IP[:], ip)
}
if cm.IfIndex != 0 {
pi.IfIndex = int32(cm.IfIndex)
}
}
return
}
2 changes: 1 addition & 1 deletion ipv4/control_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 plan9 solaris
// +build plan9 solaris

package ipv4

Expand Down
Loading

0 comments on commit 38dcae4

Please sign in to comment.