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
diff --git a/ipv4/control.go b/ipv4/control.go
index 56d906f..6d1a49d 100644
--- a/ipv4/control.go
+++ b/ipv4/control.go
@@ -11,15 +11,13 @@
 )
 
 type rawOpt struct {
-	mu     sync.Mutex
+	sync.Mutex
 	cflags ControlFlags
 }
 
-func (o *rawOpt) lock()                     { o.mu.Lock() }
-func (o *rawOpt) unlock()                   { o.mu.Unlock() }
-func (o *rawOpt) set(f ControlFlags)        { o.cflags |= f }
-func (o *rawOpt) clear(f ControlFlags)      { o.cflags &^= f }
-func (o *rawOpt) isset(f ControlFlags) bool { return o.cflags&f != 0 }
+func (c *rawOpt) set(f ControlFlags)        { c.cflags |= f }
+func (c *rawOpt) clear(f ControlFlags)      { c.cflags &^= f }
+func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
 
 type ControlFlags uint
 
@@ -27,16 +25,23 @@
 	FlagTTL       ControlFlags = 1 << iota // pass the TTL on the received packet
 	FlagSrc                                // pass the source 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
-// packet IP-level option data.
+// A ControlMessage represents per packet basis IP-level socket options.
 type ControlMessage struct {
-	TTL     int    // time-to-live
-	Src     net.IP // source address
-	Dst     net.IP // destination address
-	IfIndex int    // interface index
+	// Receiving socket options: SetControlMessage allows to
+	// receive the options from the protocol stack using ReadFrom
+	// method of PacketConn or RawConn.
+	//
+	// 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 {
diff --git a/ipv4/control_bsd.go b/ipv4/control_bsd.go
index cd9dc87..6b1b557 100644
--- a/ipv4/control_bsd.go
+++ b/ipv4/control_bsd.go
@@ -14,8 +14,8 @@
 )
 
 func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
-	opt.lock()
-	defer opt.unlock()
+	opt.Lock()
+	defer opt.Unlock()
 	if cf&FlagTTL != 0 {
 		if err := setIPv4ReceiveTTL(fd, on); err != nil {
 			return err
@@ -50,43 +50,53 @@
 }
 
 func newControlMessage(opt *rawOpt) (oob []byte) {
-	opt.lock()
-	defer opt.unlock()
+	opt.Lock()
+	defer opt.Unlock()
+	l, off := 0, 0
 	if opt.isset(FlagTTL) {
-		b := make([]byte, 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...)
+		l += syscall.CmsgSpace(1)
 	}
 	if opt.isset(FlagDst) {
-		b := make([]byte, 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...)
+		l += syscall.CmsgSpace(net.IPv4len)
 	}
 	if opt.isset(FlagInterface) {
-		b := make([]byte, syscall.CmsgSpace(syscall.SizeofSockaddrDatalink))
-		cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
-		cmsg.Level = ianaProtocolIP
-		cmsg.Type = syscall.IP_RECVIF
-		cmsg.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink))
-		oob = append(oob, b...)
+		l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink)
+	}
+	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(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
 }
 
 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)
 	}
-	if len(b) == 0 {
-		return nil, nil
-	}
 	cm := &ControlMessage{}
 	for _, m := range cmsgs {
 		if m.Header.Level != ianaProtocolIP {
@@ -96,8 +106,7 @@
 		case syscall.IP_RECVTTL:
 			cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
 		case syscall.IP_RECVDSTADDR:
-			v := m.Data[:4]
-			cm.Dst = net.IPv4(v[0], v[1], v[2], v[3])
+			cm.Dst = m.Data[:net.IPv4len]
 		case syscall.IP_RECVIF:
 			sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0]))
 			cm.IfIndex = int(sadl.Index)
diff --git a/ipv4/control_linux.go b/ipv4/control_linux.go
index cf2dcf0..fa7630f 100644
--- a/ipv4/control_linux.go
+++ b/ipv4/control_linux.go
@@ -5,7 +5,6 @@
 package ipv4
 
 import (
-	"net"
 	"os"
 	"syscall"
 	"unsafe"
@@ -16,8 +15,8 @@
 const pktinfo = FlagSrc | FlagDst | FlagInterface
 
 func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
-	opt.lock()
-	defer opt.unlock()
+	opt.Lock()
+	defer opt.Unlock()
 	if cf&FlagTTL != 0 {
 		if err := setIPv4ReceiveTTL(fd, on); err != nil {
 			return err
@@ -42,35 +41,43 @@
 }
 
 func newControlMessage(opt *rawOpt) (oob []byte) {
-	opt.lock()
-	defer opt.unlock()
+	opt.Lock()
+	defer opt.Unlock()
+	l, off := 0, 0
 	if opt.isset(FlagTTL) {
-		b := make([]byte, 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...)
+		l += syscall.CmsgSpace(1)
 	}
 	if opt.isset(pktinfo) {
-		b := make([]byte, syscall.CmsgSpace(syscall.SizeofInet4Pktinfo))
-		cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
-		cmsg.Level = ianaProtocolIP
-		cmsg.Type = syscall.IP_PKTINFO
-		cmsg.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
-		oob = append(oob, b...)
+		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)
+		}
 	}
 	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)
 	}
-	if len(b) == 0 {
-		return nil, nil
-	}
 	cm := &ControlMessage{}
 	for _, m := range cmsgs {
 		if m.Header.Level != ianaProtocolIP {
@@ -82,7 +89,7 @@
 		case syscall.IP_PKTINFO:
 			pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0]))
 			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
@@ -92,25 +99,28 @@
 	if cm == nil {
 		return
 	}
-	pi := &syscall.Inet4Pktinfo{}
+	l, off := 0, 0
 	pion := false
-	if ip := cm.Src.To4(); ip != nil {
-		copy(pi.Spec_dst[:], ip[:net.IPv4len])
+	if cm.Src.To4() != nil || cm.IfIndex != 0 {
 		pion = true
+		l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
 	}
-	if cm.IfIndex != 0 {
-		pi.Ifindex = int32(cm.IfIndex)
-		pion = true
-	}
-	if pion {
-		b := make([]byte, syscall.CmsgSpace(syscall.SizeofInet4Pktinfo))
-		cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
-		cmsg.Level = ianaProtocolIP
-		cmsg.Type = syscall.IP_PKTINFO
-		cmsg.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
-		data := b[syscall.CmsgLen(0):]
-		copy(data[:syscall.SizeofInet4Pktinfo], (*[syscall.SizeofInet4Pktinfo]byte)(unsafe.Pointer(pi))[:syscall.SizeofInet4Pktinfo])
-		oob = append(oob, b...)
+	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)
+		}
 	}
 	return
 }
diff --git a/ipv4/control_plan9.go b/ipv4/control_plan9.go
index df9d50b..18feaaa 100644
--- a/ipv4/control_plan9.go
+++ b/ipv4/control_plan9.go
@@ -4,9 +4,7 @@
 
 package ipv4
 
-import (
-	"syscall"
-)
+import "syscall"
 
 func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
 	// TODO(mikio): Implement this
diff --git a/ipv4/control_windows.go b/ipv4/control_windows.go
index 7b1d604..5a68a55 100644
--- a/ipv4/control_windows.go
+++ b/ipv4/control_windows.go
@@ -4,9 +4,7 @@
 
 package ipv4
 
-import (
-	"syscall"
-)
+import "syscall"
 
 func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
 	// TODO(mikio): Implement this