Commit ecb7ecda authored by Mikio Hara's avatar Mikio Hara

go.net/ipv4: simplify ancillary helpers

This CL reduces unnecessary allocations in ancillary helper
functions. Also clarifies documentation on ControlMessage,
ControlFlags.

benchmark                     old allocs   new allocs    delta
BenchmarkReadWriteNetUDP-2             6            6    0.00%
BenchmarkReadWriteIPv4UDP-2           17           15  -11.76%

R=dave
CC=golang-dev
https://golang.org/cl/9232050
parent 9d071032
...@@ -11,15 +11,13 @@ import ( ...@@ -11,15 +11,13 @@ import (
) )
type rawOpt struct { type rawOpt struct {
mu sync.Mutex sync.Mutex
cflags ControlFlags cflags ControlFlags
} }
func (o *rawOpt) lock() { o.mu.Lock() } func (c *rawOpt) set(f ControlFlags) { c.cflags |= f }
func (o *rawOpt) unlock() { o.mu.Unlock() } func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f }
func (o *rawOpt) set(f ControlFlags) { o.cflags |= f } func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
func (o *rawOpt) clear(f ControlFlags) { o.cflags &^= f }
func (o *rawOpt) isset(f ControlFlags) bool { return o.cflags&f != 0 }
type ControlFlags uint type ControlFlags uint
...@@ -27,16 +25,23 @@ const ( ...@@ -27,16 +25,23 @@ const (
FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet
FlagSrc // pass the source address on the received packet FlagSrc // pass the source address on the received packet
FlagDst // pass the destination address on the received packet FlagDst // pass the destination address on the received packet
FlagInterface // pass the interface index on the received packet or outgoing packet FlagInterface // pass the interface index on the received packet
) )
// A ControlMessage represents control information that contains per // A ControlMessage represents per packet basis IP-level socket options.
// packet IP-level option data.
type ControlMessage struct { type ControlMessage struct {
TTL int // time-to-live // Receiving socket options: SetControlMessage allows to
Src net.IP // source address // receive the options from the protocol stack using ReadFrom
Dst net.IP // destination address // method of PacketConn or RawConn.
IfIndex int // interface index //
// Specifying socket options: ControlMessage for WriteTo
// method of PacketConn or RawConn allows to send the options
// to the protocol stack.
//
TTL int // time-to-live, receiving only
Src net.IP // source address, specifying only
Dst net.IP // destination address, receiving only
IfIndex int // interface index, must be 1 <= value when specifying
} }
func (cm *ControlMessage) String() string { func (cm *ControlMessage) String() string {
......
...@@ -14,8 +14,8 @@ import ( ...@@ -14,8 +14,8 @@ import (
) )
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.lock() opt.Lock()
defer opt.unlock() defer opt.Unlock()
if cf&FlagTTL != 0 { if cf&FlagTTL != 0 {
if err := setIPv4ReceiveTTL(fd, on); err != nil { if err := setIPv4ReceiveTTL(fd, on); err != nil {
return err return err
...@@ -50,43 +50,53 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { ...@@ -50,43 +50,53 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
} }
func newControlMessage(opt *rawOpt) (oob []byte) { func newControlMessage(opt *rawOpt) (oob []byte) {
opt.lock() opt.Lock()
defer opt.unlock() defer opt.Unlock()
l, off := 0, 0
if opt.isset(FlagTTL) { if opt.isset(FlagTTL) {
b := make([]byte, syscall.CmsgSpace(1)) l += syscall.CmsgSpace(1)
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = ianaProtocolIP
cmsg.Type = syscall.IP_RECVTTL
cmsg.SetLen(syscall.CmsgLen(1))
oob = append(oob, b...)
} }
if opt.isset(FlagDst) { if opt.isset(FlagDst) {
b := make([]byte, syscall.CmsgSpace(net.IPv4len)) l += syscall.CmsgSpace(net.IPv4len)
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = ianaProtocolIP
cmsg.Type = syscall.IP_RECVDSTADDR
cmsg.SetLen(syscall.CmsgLen(net.IPv4len))
oob = append(oob, b...)
} }
if opt.isset(FlagInterface) { if opt.isset(FlagInterface) {
b := make([]byte, syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)) l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) }
cmsg.Level = ianaProtocolIP if l > 0 {
cmsg.Type = syscall.IP_RECVIF oob = make([]byte, l)
cmsg.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink)) if opt.isset(FlagTTL) {
oob = append(oob, b...) 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(FlagDst) {
m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
m.Level = ianaProtocolIP
m.Type = syscall.IP_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 = syscall.IP_RECVIF
m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
}
} }
return return
} }
func parseControlMessage(b []byte) (*ControlMessage, error) { func parseControlMessage(b []byte) (*ControlMessage, error) {
if len(b) == 0 {
return nil, nil
}
cmsgs, err := syscall.ParseSocketControlMessage(b) cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil { if err != nil {
return nil, os.NewSyscallError("parse socket control message", err) return nil, os.NewSyscallError("parse socket control message", err)
} }
if len(b) == 0 {
return nil, nil
}
cm := &ControlMessage{} cm := &ControlMessage{}
for _, m := range cmsgs { for _, m := range cmsgs {
if m.Header.Level != ianaProtocolIP { if m.Header.Level != ianaProtocolIP {
...@@ -96,8 +106,7 @@ func parseControlMessage(b []byte) (*ControlMessage, error) { ...@@ -96,8 +106,7 @@ func parseControlMessage(b []byte) (*ControlMessage, error) {
case syscall.IP_RECVTTL: case syscall.IP_RECVTTL:
cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
case syscall.IP_RECVDSTADDR: case syscall.IP_RECVDSTADDR:
v := m.Data[:4] cm.Dst = m.Data[:net.IPv4len]
cm.Dst = net.IPv4(v[0], v[1], v[2], v[3])
case syscall.IP_RECVIF: case syscall.IP_RECVIF:
sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0])) sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(sadl.Index) cm.IfIndex = int(sadl.Index)
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
package ipv4 package ipv4
import ( import (
"net"
"os" "os"
"syscall" "syscall"
"unsafe" "unsafe"
...@@ -16,8 +15,8 @@ import ( ...@@ -16,8 +15,8 @@ import (
const pktinfo = FlagSrc | FlagDst | FlagInterface const pktinfo = FlagSrc | FlagDst | FlagInterface
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
opt.lock() opt.Lock()
defer opt.unlock() defer opt.Unlock()
if cf&FlagTTL != 0 { if cf&FlagTTL != 0 {
if err := setIPv4ReceiveTTL(fd, on); err != nil { if err := setIPv4ReceiveTTL(fd, on); err != nil {
return err return err
...@@ -42,35 +41,43 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { ...@@ -42,35 +41,43 @@ func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
} }
func newControlMessage(opt *rawOpt) (oob []byte) { func newControlMessage(opt *rawOpt) (oob []byte) {
opt.lock() opt.Lock()
defer opt.unlock() defer opt.Unlock()
l, off := 0, 0
if opt.isset(FlagTTL) { if opt.isset(FlagTTL) {
b := make([]byte, syscall.CmsgSpace(1)) l += syscall.CmsgSpace(1)
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
cmsg.Level = ianaProtocolIP
cmsg.Type = syscall.IP_RECVTTL
cmsg.SetLen(syscall.CmsgLen(1))
oob = append(oob, b...)
} }
if opt.isset(pktinfo) { if opt.isset(pktinfo) {
b := make([]byte, syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)) l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) }
cmsg.Level = ianaProtocolIP if l > 0 {
cmsg.Type = syscall.IP_PKTINFO oob = make([]byte, l)
cmsg.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) if opt.isset(FlagTTL) {
oob = append(oob, b...) 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)
}
} }
return return
} }
func parseControlMessage(b []byte) (*ControlMessage, error) { func parseControlMessage(b []byte) (*ControlMessage, error) {
if len(b) == 0 {
return nil, nil
}
cmsgs, err := syscall.ParseSocketControlMessage(b) cmsgs, err := syscall.ParseSocketControlMessage(b)
if err != nil { if err != nil {
return nil, os.NewSyscallError("parse socket control message", err) return nil, os.NewSyscallError("parse socket control message", err)
} }
if len(b) == 0 {
return nil, nil
}
cm := &ControlMessage{} cm := &ControlMessage{}
for _, m := range cmsgs { for _, m := range cmsgs {
if m.Header.Level != ianaProtocolIP { if m.Header.Level != ianaProtocolIP {
...@@ -82,7 +89,7 @@ func parseControlMessage(b []byte) (*ControlMessage, error) { ...@@ -82,7 +89,7 @@ func parseControlMessage(b []byte) (*ControlMessage, error) {
case syscall.IP_PKTINFO: case syscall.IP_PKTINFO:
pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0])) pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0]))
cm.IfIndex = int(pi.Ifindex) cm.IfIndex = int(pi.Ifindex)
cm.Dst = net.IPv4(pi.Addr[0], pi.Addr[1], pi.Addr[2], pi.Addr[3]) cm.Dst = pi.Addr[:]
} }
} }
return cm, nil return cm, nil
...@@ -92,25 +99,28 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) { ...@@ -92,25 +99,28 @@ func marshalControlMessage(cm *ControlMessage) (oob []byte) {
if cm == nil { if cm == nil {
return return
} }
pi := &syscall.Inet4Pktinfo{} l, off := 0, 0
pion := false pion := false
if ip := cm.Src.To4(); ip != nil { if cm.Src.To4() != nil || cm.IfIndex != 0 {
copy(pi.Spec_dst[:], ip[:net.IPv4len])
pion = true pion = true
l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
} }
if cm.IfIndex != 0 { if l > 0 {
pi.Ifindex = int32(cm.IfIndex) oob = make([]byte, l)
pion = true if pion {
} m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
if pion { m.Level = ianaProtocolIP
b := make([]byte, syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)) m.Type = syscall.IP_PKTINFO
cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
cmsg.Level = ianaProtocolIP pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
cmsg.Type = syscall.IP_PKTINFO if ip := cm.Src.To4(); ip != nil {
cmsg.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) copy(pi.Addr[:], ip)
data := b[syscall.CmsgLen(0):] }
copy(data[:syscall.SizeofInet4Pktinfo], (*[syscall.SizeofInet4Pktinfo]byte)(unsafe.Pointer(pi))[:syscall.SizeofInet4Pktinfo]) if cm.IfIndex != 0 {
oob = append(oob, b...) pi.Ifindex = int32(cm.IfIndex)
}
off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
}
} }
return return
} }
...@@ -4,9 +4,7 @@ ...@@ -4,9 +4,7 @@
package ipv4 package ipv4
import ( import "syscall"
"syscall"
)
func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): Implement this // TODO(mikio): Implement this
......
...@@ -4,9 +4,7 @@ ...@@ -4,9 +4,7 @@
package ipv4 package ipv4
import ( import "syscall"
"syscall"
)
func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error { func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): Implement this // TODO(mikio): Implement this
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment