go.net/ipv4: add support for dragonfly, enable IP_PKTINFO on darwin

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
diff --git a/ipv4/control_bsd.go b/ipv4/control_bsd.go
index 6b1b557..38ef01f 100644
--- a/ipv4/control_bsd.go
+++ b/ipv4/control_bsd.go
@@ -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"
 )
@@ -26,62 +25,92 @@
 			opt.clear(FlagTTL)
 		}
 	}
-	if cf&FlagDst != 0 {
-		if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil {
-			return err
+	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 on {
-			opt.set(FlagDst)
-		} else {
-			opt.clear(FlagDst)
+	} else {
+		if cf&FlagDst != 0 {
+			if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil {
+				return err
+			}
+			if on {
+				opt.set(FlagDst)
+			} else {
+				opt.clear(FlagDst)
+			}
 		}
-	}
-	if cf&FlagInterface != 0 {
-		if err := setIPv4ReceiveInterface(fd, on); err != nil {
-			return err
-		}
-		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)
 		}
@@ -89,33 +118,25 @@
 	return
 }
 
-func parseControlMessage(b []byte) (*ControlMessage, error) {
-	if len(b) == 0 {
-		return nil, nil
+func (cm *ControlMessage) oobLen() (l int) {
+	if supportsPacketInfo && (cm.Src.To4() != nil || cm.IfIndex != 0) {
+		l += syscall.CmsgSpace(sysSizeofPacketInfo)
 	}
-	cmsgs, err := syscall.ParseSocketControlMessage(b)
-	if err != nil {
-		return nil, os.NewSyscallError("parse socket control message", err)
-	}
-	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[:]
+	}
 }
diff --git a/ipv4/control_linux.go b/ipv4/control_linux.go
index fa7630f..5685367 100644
--- a/ipv4/control_linux.go
+++ b/ipv4/control_linux.go
@@ -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()
@@ -27,100 +22,63 @@
 			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[:]
+	}
+}
diff --git a/ipv4/control_nonpktinfo.go b/ipv4/control_nonpktinfo.go
new file mode 100644
index 0000000..c7da635
--- /dev/null
+++ b/ipv4/control_nonpktinfo.go
@@ -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
+}
diff --git a/ipv4/control_pktinfo.go b/ipv4/control_pktinfo.go
new file mode 100644
index 0000000..4778ef1
--- /dev/null
+++ b/ipv4/control_pktinfo.go
@@ -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
+}
diff --git a/ipv4/control_stub.go b/ipv4/control_stub.go
index 2b043a5..49b7f5c 100644
--- a/ipv4/control_stub.go
+++ b/ipv4/control_stub.go
@@ -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
 
diff --git a/ipv4/control_unix.go b/ipv4/control_unix.go
new file mode 100644
index 0000000..f3dd2f5
--- /dev/null
+++ b/ipv4/control_unix.go
@@ -0,0 +1,43 @@
+// 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package ipv4
+
+import (
+	"os"
+	"syscall"
+)
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	opt.Lock()
+	defer opt.Unlock()
+	return opt.marshalControlMessage()
+}
+
+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)
+	}
+	cm := &ControlMessage{}
+	for _, m := range cmsgs {
+		if m.Header.Level != ianaProtocolIP {
+			continue
+		}
+		cm.parseControlMessage(&m)
+	}
+	return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	if cm == nil {
+		return nil
+	}
+	return cm.marshalPacketInfo()
+}
diff --git a/ipv4/dgramopt_posix.go b/ipv4/dgramopt_posix.go
index 0ba81c2..874afde 100644
--- a/ipv4/dgramopt_posix.go
+++ b/ipv4/dgramopt_posix.go
@@ -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 darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
 
 package ipv4
 
diff --git a/ipv4/dgramopt_stub.go b/ipv4/dgramopt_stub.go
index b961a9a..794048f 100644
--- a/ipv4/dgramopt_stub.go
+++ b/ipv4/dgramopt_stub.go
@@ -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
 
diff --git a/ipv4/genericopt_posix.go b/ipv4/genericopt_posix.go
index f1eab2d..483c53a 100644
--- a/ipv4/genericopt_posix.go
+++ b/ipv4/genericopt_posix.go
@@ -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 darwin freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows
 
 package ipv4
 
diff --git a/ipv4/genericopt_stub.go b/ipv4/genericopt_stub.go
index b7bf373..342dfcf 100644
--- a/ipv4/genericopt_stub.go
+++ b/ipv4/genericopt_stub.go
@@ -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
 
diff --git a/ipv4/helper_posix.go b/ipv4/helper_posix.go
deleted file mode 100644
index 0adb3e1..0000000
--- a/ipv4/helper_posix.go
+++ /dev/null
@@ -1,42 +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.
-
-// +build darwin freebsd linux netbsd openbsd windows
-
-package ipv4
-
-import (
-	"bytes"
-	"net"
-	"syscall"
-)
-
-func setSyscallIPMreq(mreq *syscall.IPMreq, ifi *net.Interface) error {
-	if ifi == nil {
-		return nil
-	}
-	ifat, err := ifi.Addrs()
-	if err != nil {
-		return err
-	}
-	for _, ifa := range ifat {
-		switch v := ifa.(type) {
-		case *net.IPAddr:
-			if a := v.IP.To4(); a != nil {
-				copy(mreq.Interface[:], a)
-				goto done
-			}
-		case *net.IPNet:
-			if a := v.IP.To4(); a != nil {
-				copy(mreq.Interface[:], a)
-				goto done
-			}
-		}
-	}
-done:
-	if bytes.Equal(mreq.Multiaddr[:], net.IPv4zero.To4()) {
-		return errNoSuchMulticastInterface
-	}
-	return nil
-}
diff --git a/ipv4/helper_stub.go b/ipv4/helper_stub.go
index 3024860..03432d3 100644
--- a/ipv4/helper_stub.go
+++ b/ipv4/helper_stub.go
@@ -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
 
diff --git a/ipv4/helper_unix.go b/ipv4/helper_unix.go
index 3c12593..345ca7d 100644
--- a/ipv4/helper_unix.go
+++ b/ipv4/helper_unix.go
@@ -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 darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
 
 package ipv4
 
diff --git a/ipv4/packet.go b/ipv4/packet.go
index faeeec0..0986431 100644
--- a/ipv4/packet.go
+++ b/ipv4/packet.go
@@ -56,8 +56,8 @@
 // WriteTo writes an IPv4 datagram through the endpoint c, copying the
 // datagram from the IPv4 header h and the payload p.  The control
 // message cm allows the datagram path and the outgoing interface to be
-// specified.  Currently only Linux supports this.  The cm may be nil
-// if control of the outgoing datagram is not required.
+// specified.  Currently only Darwin and Linux support this.  The cm
+// may be nil if control of the outgoing datagram is not required.
 //
 // The IPv4 header h must contain appropriate fields that include:
 //
diff --git a/ipv4/payload.go b/ipv4/payload.go
index 39efb4c..d7698cb 100644
--- a/ipv4/payload.go
+++ b/ipv4/payload.go
@@ -4,10 +4,7 @@
 
 package ipv4
 
-import (
-	"net"
-	"syscall"
-)
+import "net"
 
 // A payloadHandler represents the IPv4 datagram payload handler.
 type payloadHandler struct {
@@ -16,66 +13,3 @@
 }
 
 func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil }
-
-// ReadFrom reads a payload of the received IPv4 datagram, from the
-// endpoint c, copying the payload into b.  It returns the number of
-// bytes copied into b, the control message cm and the source address
-// src of the received datagram.
-func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
-	if !c.ok() {
-		return 0, nil, nil, syscall.EINVAL
-	}
-	oob := newControlMessage(&c.rawOpt)
-	var oobn int
-	switch c := c.PacketConn.(type) {
-	case *net.UDPConn:
-		if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
-			return 0, nil, nil, err
-		}
-	case *net.IPConn:
-		nb := make([]byte, maxHeaderLen+len(b))
-		if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil {
-			return 0, nil, nil, err
-		}
-		hdrlen := int(nb[0]&0x0f) << 2
-		copy(b, nb[hdrlen:])
-		n -= hdrlen
-	default:
-		return 0, nil, nil, errInvalidConnType
-	}
-	if cm, err = parseControlMessage(oob[:oobn]); err != nil {
-		return 0, nil, nil, err
-	}
-	if cm != nil {
-		cm.Src = netAddrToIP4(src)
-	}
-	return
-}
-
-// WriteTo writes a payload of the IPv4 datagram, to the destination
-// address dst through the endpoint c, copying the payload from b.  It
-// returns the number of bytes written.  The control message cm allows
-// the datagram path and the outgoing interface to be specified.
-// Currently only Linux supports this.  The cm may be nil if control
-// of the outgoing datagram is not required.
-func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
-	if !c.ok() {
-		return 0, syscall.EINVAL
-	}
-	oob := marshalControlMessage(cm)
-	if dst == nil {
-		return 0, errMissingAddress
-	}
-	switch c := c.PacketConn.(type) {
-	case *net.UDPConn:
-		n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
-	case *net.IPConn:
-		n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr))
-	default:
-		return 0, errInvalidConnType
-	}
-	if err != nil {
-		return 0, err
-	}
-	return
-}
diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go
new file mode 100644
index 0000000..2cda8b9
--- /dev/null
+++ b/ipv4/payload_cmsg.go
@@ -0,0 +1,75 @@
+// 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.
+
+// +build !plan9,!solaris,!windows
+
+package ipv4
+
+import (
+	"net"
+	"syscall"
+)
+
+// ReadFrom reads a payload of the received IPv4 datagram, from the
+// endpoint c, copying the payload into b.  It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+	if !c.ok() {
+		return 0, nil, nil, syscall.EINVAL
+	}
+	oob := newControlMessage(&c.rawOpt)
+	var oobn int
+	switch c := c.PacketConn.(type) {
+	case *net.UDPConn:
+		if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
+			return 0, nil, nil, err
+		}
+	case *net.IPConn:
+		nb := make([]byte, maxHeaderLen+len(b))
+		if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil {
+			return 0, nil, nil, err
+		}
+		hdrlen := int(nb[0]&0x0f) << 2
+		copy(b, nb[hdrlen:])
+		n -= hdrlen
+	default:
+		return 0, nil, nil, errInvalidConnType
+	}
+	if cm, err = parseControlMessage(oob[:oobn]); err != nil {
+		return 0, nil, nil, err
+	}
+	if cm != nil {
+		cm.Src = netAddrToIP4(src)
+	}
+	return
+}
+
+// WriteTo writes a payload of the IPv4 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b.  It
+// returns the number of bytes written.  The control message cm allows
+// the datagram path and the outgoing interface to be specified.
+// Currently only Darwin and Darwin support this.  The cm may be nil if
+// control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	oob := marshalControlMessage(cm)
+	if dst == nil {
+		return 0, errMissingAddress
+	}
+	switch c := c.PacketConn.(type) {
+	case *net.UDPConn:
+		n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
+	case *net.IPConn:
+		n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr))
+	default:
+		return 0, errInvalidConnType
+	}
+	if err != nil {
+		return 0, err
+	}
+	return
+}
diff --git a/ipv4/payload_noncmsg.go b/ipv4/payload_noncmsg.go
new file mode 100644
index 0000000..d128c9c
--- /dev/null
+++ b/ipv4/payload_noncmsg.go
@@ -0,0 +1,42 @@
+// 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.
+
+// +build plan9 solaris windows
+
+package ipv4
+
+import (
+	"net"
+	"syscall"
+)
+
+// ReadFrom reads a payload of the received IPv4 datagram, from the
+// endpoint c, copying the payload into b.  It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+	if !c.ok() {
+		return 0, nil, nil, syscall.EINVAL
+	}
+	if n, src, err = c.PacketConn.ReadFrom(b); err != nil {
+		return 0, nil, nil, err
+	}
+	return
+}
+
+// WriteTo writes a payload of the IPv4 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b.  It
+// returns the number of bytes written.  The control message cm allows
+// the datagram path and the outgoing interface to be specified.
+// Currently only Darwin and Linux support this.  The cm may be nil if
+// control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	if dst == nil {
+		return 0, errMissingAddress
+	}
+	return c.PacketConn.WriteTo(b, dst)
+}
diff --git a/ipv4/sockopt_bsd.go b/ipv4/sockopt_bsd.go
index 71e0bf0..b793f5c 100644
--- a/ipv4/sockopt_bsd.go
+++ b/ipv4/sockopt_bsd.go
@@ -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 darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd netbsd openbsd
 
 package ipv4
 
@@ -13,7 +13,7 @@
 )
 
 func ipv4MulticastTTL(fd int) (int, error) {
-	v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL)
+	v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastTTL)
 	if err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
@@ -21,11 +21,11 @@
 }
 
 func setIPv4MulticastTTL(fd int, v int) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, byte(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastTTL, byte(v)))
 }
 
 func ipv4ReceiveDestinationAddress(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVDSTADDR)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveDst)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -33,11 +33,11 @@
 }
 
 func setIPv4ReceiveDestinationAddress(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVDSTADDR, boolint(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveDst, boolint(v)))
 }
 
 func ipv4ReceiveInterface(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVIF)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveInterface)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -45,11 +45,11 @@
 }
 
 func setIPv4ReceiveInterface(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVIF, boolint(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveInterface, boolint(v)))
 }
 
 func ipv4MulticastInterface(fd int) (*net.Interface, error) {
-	v, err := syscall.GetsockoptInet4Addr(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF)
+	v, err := syscall.GetsockoptInet4Addr(fd, ianaProtocolIP, sysSockoptMulticastInterface)
 	if err != nil {
 		return nil, os.NewSyscallError("getsockopt", err)
 	}
@@ -63,11 +63,11 @@
 	}
 	var v [4]byte
 	copy(v[:], ip.To4())
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, v))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd, ianaProtocolIP, sysSockoptMulticastInterface, v))
 }
 
 func ipv4MulticastLoopback(fd int) (bool, error) {
-	v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP)
+	v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastLoopback)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -75,21 +75,5 @@
 }
 
 func setIPv4MulticastLoopback(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))))
-}
-
-func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
-	mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
-	if err := setSyscallIPMreq(&mreq, ifi); err != nil {
-		return err
-	}
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, &mreq))
-}
-
-func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
-	mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
-	if err := setSyscallIPMreq(&mreq, ifi); err != nil {
-		return err
-	}
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, &mreq))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, sysSockoptMulticastLoopback, byte(boolint(v))))
 }
diff --git a/ipv4/sockopt_freebsd.go b/ipv4/sockopt_freebsd.go
deleted file mode 100644
index 55c6de9..0000000
--- a/ipv4/sockopt_freebsd.go
+++ /dev/null
@@ -1,22 +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 (
-	"os"
-	"syscall"
-)
-
-func ipv4SendSourceAddress(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_SENDSRCADDR)
-	if err != nil {
-		return false, os.NewSyscallError("getsockopt", err)
-	}
-	return v == 1, nil
-}
-
-func setIPv4SendSourceAddress(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_SENDSRCADDR, boolint(v)))
-}
diff --git a/ipv4/sockopt_linux.go b/ipv4/sockopt_linux.go
index cb5f6db..6ec58de 100644
--- a/ipv4/sockopt_linux.go
+++ b/ipv4/sockopt_linux.go
@@ -11,7 +11,7 @@
 )
 
 func ipv4ReceiveTOS(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTOS)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTOS)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -19,11 +19,11 @@
 }
 
 func setIPv4ReceiveTOS(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTOS, boolint(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTOS, boolint(v)))
 }
 
 func ipv4MulticastTTL(fd int) (int, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastTTL)
 	if err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
@@ -31,42 +31,30 @@
 }
 
 func setIPv4MulticastTTL(fd int, v int) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, v))
-}
-
-func ipv4PacketInfo(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_PKTINFO)
-	if err != nil {
-		return false, os.NewSyscallError("getsockopt", err)
-	}
-	return v == 1, nil
-}
-
-func setIPv4PacketInfo(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_PKTINFO, boolint(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastTTL, v))
 }
 
 func ipv4MulticastInterface(fd int) (*net.Interface, error) {
-	mreqn, err := syscall.GetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF)
+	mreqn, err := syscall.GetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptMulticastInterface)
 	if err != nil {
 		return nil, os.NewSyscallError("getsockopt", err)
 	}
-	if int(mreqn.Ifindex) == 0 {
+	if mreqn.Ifindex == 0 {
 		return nil, nil
 	}
 	return net.InterfaceByIndex(int(mreqn.Ifindex))
 }
 
 func setIPv4MulticastInterface(fd int, ifi *net.Interface) error {
-	mreqn := syscall.IPMreqn{}
+	var mreqn syscall.IPMreqn
 	if ifi != nil {
 		mreqn.Ifindex = int32(ifi.Index)
 	}
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, &mreqn))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptMulticastInterface, &mreqn))
 }
 
 func ipv4MulticastLoopback(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastLoopback)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -74,21 +62,5 @@
 }
 
 func setIPv4MulticastLoopback(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, boolint(v)))
-}
-
-func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
-	mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
-	if ifi != nil {
-		mreqn.Ifindex = int32(ifi.Index)
-	}
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, &mreqn))
-}
-
-func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
-	mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
-	if ifi != nil {
-		mreqn.Ifindex = int32(ifi.Index)
-	}
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, &mreqn))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, boolint(v)))
 }
diff --git a/ipv4/sockopt_mreq.go b/ipv4/sockopt_mreq.go
new file mode 100644
index 0000000..6e591ca
--- /dev/null
+++ b/ipv4/sockopt_mreq.go
@@ -0,0 +1,29 @@
+// 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.
+
+// +build darwin dragonfly netbsd openbsd
+
+package ipv4
+
+import (
+	"net"
+	"os"
+	"syscall"
+)
+
+func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
+	mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+	if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
+		return err
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, sysSockoptJoinGroup, &mreq))
+}
+
+func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
+	mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+	if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
+		return err
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, sysSockoptLeaveGroup, &mreq))
+}
diff --git a/ipv4/sockopt_mreqn.go b/ipv4/sockopt_mreqn.go
new file mode 100644
index 0000000..a3a87d7
--- /dev/null
+++ b/ipv4/sockopt_mreqn.go
@@ -0,0 +1,29 @@
+// 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 freebsd linux
+
+package ipv4
+
+import (
+	"net"
+	"os"
+	"syscall"
+)
+
+func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
+	mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+	if ifi != nil {
+		mreqn.Ifindex = int32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptJoinGroup, &mreqn))
+}
+
+func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error {
+	mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
+	if ifi != nil {
+		mreqn.Ifindex = int32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, sysSockoptLeaveGroup, &mreqn))
+}
diff --git a/ipv4/sockopt_nonpktinfo.go b/ipv4/sockopt_nonpktinfo.go
new file mode 100644
index 0000000..5592b46
--- /dev/null
+++ b/ipv4/sockopt_nonpktinfo.go
@@ -0,0 +1,15 @@
+// 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 ipv4PacketInfo(fd int) (bool, error) {
+	return false, errOpNoSupport
+}
+
+func setIPv4PacketInfo(fd int, v bool) error {
+	return errOpNoSupport
+}
diff --git a/ipv4/sockopt_pktinfo.go b/ipv4/sockopt_pktinfo.go
new file mode 100644
index 0000000..5506b88
--- /dev/null
+++ b/ipv4/sockopt_pktinfo.go
@@ -0,0 +1,24 @@
+// 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 (
+	"os"
+	"syscall"
+)
+
+func ipv4PacketInfo(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptPacketInfo)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv4PacketInfo(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptPacketInfo, boolint(v)))
+}
diff --git a/ipv4/sockopt_stub.go b/ipv4/sockopt_stub.go
index fd9b9fd..53ae402 100644
--- a/ipv4/sockopt_stub.go
+++ b/ipv4/sockopt_stub.go
@@ -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
 
diff --git a/ipv4/sockopt_unix.go b/ipv4/sockopt_unix.go
index 3301656..a96fe25 100644
--- a/ipv4/sockopt_unix.go
+++ b/ipv4/sockopt_unix.go
@@ -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 darwin freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
 
 package ipv4
 
@@ -12,7 +12,7 @@
 )
 
 func ipv4TOS(fd int) (int, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_TOS)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptTOS)
 	if err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
@@ -20,11 +20,11 @@
 }
 
 func setIPv4TOS(fd int, v int) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_TOS, v))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTOS, v))
 }
 
 func ipv4TTL(fd int) (int, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_TTL)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL)
 	if err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
@@ -32,11 +32,11 @@
 }
 
 func setIPv4TTL(fd int, v int) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_TTL, v))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptTTL, v))
 }
 
 func ipv4ReceiveTTL(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTTL)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -44,11 +44,11 @@
 }
 
 func setIPv4ReceiveTTL(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTTL, boolint(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptReceiveTTL, boolint(v)))
 }
 
 func ipv4HeaderPrepend(fd int) (bool, error) {
-	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_HDRINCL)
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend)
 	if err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
@@ -56,5 +56,5 @@
 }
 
 func setIPv4HeaderPrepend(fd int, v bool) error {
-	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_HDRINCL, boolint(v)))
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, sysSockoptHeaderPrepend, boolint(v)))
 }
diff --git a/ipv4/sockopt_windows.go b/ipv4/sockopt_windows.go
index f7ee073..e6bf593 100644
--- a/ipv4/sockopt_windows.go
+++ b/ipv4/sockopt_windows.go
@@ -17,7 +17,7 @@
 func ipv4TOS(fd syscall.Handle) (int, error) {
 	var v int32
 	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_TOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
 	return int(v), nil
@@ -25,13 +25,13 @@
 
 func setIPv4TOS(fd syscall.Handle, v int) error {
 	vv := int32(v)
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_TOS, (*byte)(unsafe.Pointer(&vv)), 4))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptTOS, (*byte)(unsafe.Pointer(&vv)), 4))
 }
 
 func ipv4TTL(fd syscall.Handle) (int, error) {
 	var v int32
 	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_TTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptTTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
 	return int(v), nil
@@ -39,13 +39,13 @@
 
 func setIPv4TTL(fd syscall.Handle, v int) error {
 	vv := int32(v)
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_TTL, (*byte)(unsafe.Pointer(&vv)), 4))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptTTL, (*byte)(unsafe.Pointer(&vv)), 4))
 }
 
 func ipv4MulticastTTL(fd syscall.Handle) (int, error) {
 	var v int32
 	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastTTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
 		return 0, os.NewSyscallError("getsockopt", err)
 	}
 	return int(v), nil
@@ -53,7 +53,7 @@
 
 func setIPv4MulticastTTL(fd syscall.Handle, v int) error {
 	vv := int32(v)
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, (*byte)(unsafe.Pointer(&vv)), 4))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastTTL, (*byte)(unsafe.Pointer(&vv)), 4))
 }
 
 func ipv4ReceiveTTL(fd syscall.Handle) (bool, error) {
@@ -99,7 +99,7 @@
 func ipv4MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
 	var v [4]byte
 	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&v[0])), &l); err != nil {
+	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v[0])), &l); err != nil {
 		return nil, os.NewSyscallError("getsockopt", err)
 	}
 	return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3]))
@@ -112,13 +112,13 @@
 	}
 	var v [4]byte
 	copy(v[:], ip.To4())
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&v[0])), 4))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v[0])), 4))
 }
 
 func ipv4MulticastLoopback(fd syscall.Handle) (bool, error) {
 	var v int32
 	l := int32(4)
-	if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+	if err := syscall.Getsockopt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil {
 		return false, os.NewSyscallError("getsockopt", err)
 	}
 	return v == 1, nil
@@ -126,21 +126,21 @@
 
 func setIPv4MulticastLoopback(fd syscall.Handle, v bool) error {
 	vv := int32(boolint(v))
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&vv)), 4))
 }
 
 func joinIPv4Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
 	mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
-	if err := setSyscallIPMreq(&mreq, ifi); err != nil {
+	if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
 		return err
 	}
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptJoinGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
 }
 
 func leaveIPv4Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
 	mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}}
-	if err := setSyscallIPMreq(&mreq, ifi); err != nil {
+	if err := setSysIPMreqInterface(&mreq, ifi); err != nil {
 		return err
 	}
-	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, sysSockoptLeaveGroup, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
 }
diff --git a/ipv4/sys.go b/ipv4/sys.go
new file mode 100644
index 0000000..31cfbe2
--- /dev/null
+++ b/ipv4/sys.go
@@ -0,0 +1,9 @@
+// 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.
+
+package ipv4
+
+// supportsPacketInfo reports whether the platform supports
+// IP_PKTINFO.
+var supportsPacketInfo bool
diff --git a/ipv4/sys_bsd.go b/ipv4/sys_bsd.go
new file mode 100644
index 0000000..866dadc
--- /dev/null
+++ b/ipv4/sys_bsd.go
@@ -0,0 +1,37 @@
+// 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 dragonfly freebsd netbsd openbsd
+
+package ipv4
+
+import "syscall"
+
+const (
+	// See /usr/include/netinet/in.h.
+	sysSockoptHeaderPrepend      = syscall.IP_HDRINCL
+	sysSockoptTOS                = syscall.IP_TOS
+	sysSockoptTTL                = syscall.IP_TTL
+	sysSockoptMulticastTTL       = syscall.IP_MULTICAST_TTL
+	sysSockoptMulticastInterface = syscall.IP_MULTICAST_IF
+	sysSockoptMulticastLoopback  = syscall.IP_MULTICAST_LOOP
+	sysSockoptJoinGroup          = syscall.IP_ADD_MEMBERSHIP
+	sysSockoptLeaveGroup         = syscall.IP_DROP_MEMBERSHIP
+)
+
+const (
+	// See /usr/include/netinet/in.h.
+	sysSockoptReceiveTTL       = syscall.IP_RECVTTL
+	sysSockoptReceiveDst       = syscall.IP_RECVDSTADDR
+	sysSockoptReceiveInterface = syscall.IP_RECVIF
+	sysSockoptPacketInfo       = 0x1a // only darwin supports this option for now
+)
+
+const sysSizeofPacketInfo = 0xc
+
+type sysPacketInfo struct {
+	IfIndex  int32
+	RoutedIP [4]byte
+	IP       [4]byte
+}
diff --git a/ipv4/sys_darwin.go b/ipv4/sys_darwin.go
new file mode 100644
index 0000000..3dee19d
--- /dev/null
+++ b/ipv4/sys_darwin.go
@@ -0,0 +1,27 @@
+// 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.
+
+package ipv4
+
+import "syscall"
+
+func init() {
+	// Seems like kern.osreldate is veiled on latest OS X. We use
+	// kern.osrelease instead.
+	osver, err := syscall.Sysctl("kern.osrelease")
+	if err != nil {
+		return
+	}
+	var i int
+	for i = range osver {
+		if osver[i] != '.' {
+			continue
+		}
+	}
+	// 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' {
+		supportsPacketInfo = true
+	}
+}
diff --git a/ipv4/sys_linux.go b/ipv4/sys_linux.go
new file mode 100644
index 0000000..a81b592
--- /dev/null
+++ b/ipv4/sys_linux.go
@@ -0,0 +1,47 @@
+// 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.
+
+package ipv4
+
+import "syscall"
+
+const (
+	// See /usr/include/linux/in.h.
+	sysSockoptHeaderPrepend      = syscall.IP_HDRINCL
+	sysSockoptTOS                = syscall.IP_TOS
+	sysSockoptTTL                = syscall.IP_TTL
+	sysSockoptMulticastTTL       = syscall.IP_MULTICAST_TTL
+	sysSockoptMulticastInterface = syscall.IP_MULTICAST_IF
+	sysSockoptMulticastLoopback  = syscall.IP_MULTICAST_LOOP
+	sysSockoptJoinGroup          = syscall.IP_ADD_MEMBERSHIP
+	sysSockoptLeaveGroup         = syscall.IP_DROP_MEMBERSHIP
+)
+
+const (
+	// See /usr/include/linux/in.h.
+	sysSockoptReceiveTOS = syscall.IP_RECVTOS
+	sysSockoptReceiveTTL = syscall.IP_RECVTTL
+	sysSockoptPacketInfo = syscall.IP_PKTINFO
+)
+
+const (
+	sysSizeofNewMulticastReq = 0xc
+	sysSizeofPacketInfo      = 0xc
+)
+
+type sysNewMulticastReq struct {
+	IP        [4]byte
+	Interface [4]byte
+	IfIndex   int32
+}
+
+type sysPacketInfo struct {
+	IfIndex  int32
+	RoutedIP [4]byte
+	IP       [4]byte
+}
+
+func init() {
+	supportsPacketInfo = true
+}
diff --git a/ipv4/sys_mreq.go b/ipv4/sys_mreq.go
new file mode 100644
index 0000000..6d5937c
--- /dev/null
+++ b/ipv4/sys_mreq.go
@@ -0,0 +1,37 @@
+// 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 dragonfly netbsd openbsd windows
+
+package ipv4
+
+import (
+	"net"
+	"syscall"
+)
+
+func setSysIPMreqInterface(mreq *syscall.IPMreq, ifi *net.Interface) error {
+	if ifi == nil {
+		return nil
+	}
+	ifat, err := ifi.Addrs()
+	if err != nil {
+		return err
+	}
+	for _, ifa := range ifat {
+		switch v := ifa.(type) {
+		case *net.IPAddr:
+			if ip := v.IP.To4(); ip != nil {
+				copy(mreq.Interface[:], ip)
+				return nil
+			}
+		case *net.IPNet:
+			if ip := v.IP.To4(); ip != nil {
+				copy(mreq.Interface[:], ip)
+				return nil
+			}
+		}
+	}
+	return errNoSuchInterface
+}
diff --git a/ipv4/sys_windows.go b/ipv4/sys_windows.go
new file mode 100644
index 0000000..1ef7f2a
--- /dev/null
+++ b/ipv4/sys_windows.go
@@ -0,0 +1,31 @@
+// 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.
+
+package ipv4
+
+import "syscall"
+
+const (
+	// See ws2tcpip.h.
+	sysSockoptHeaderPrepend      = 0x2
+	sysSockoptTOS                = syscall.IP_TOS
+	sysSockoptTTL                = syscall.IP_TTL
+	sysSockoptMulticastTTL       = syscall.IP_MULTICAST_TTL
+	sysSockoptMulticastInterface = syscall.IP_MULTICAST_IF
+	sysSockoptMulticastLoopback  = syscall.IP_MULTICAST_LOOP
+	sysSockoptJoinGroup          = syscall.IP_ADD_MEMBERSHIP
+	sysSockoptLeaveGroup         = syscall.IP_DROP_MEMBERSHIP
+)
+
+const (
+	// See ws2tcpip.h.
+	sysSockoptPacketInfo = 0x13
+)
+
+const sysSizeofPacketInfo = 0x8
+
+type sysPacketInfo struct {
+	IP      [4]byte
+	IfIndex int32
+}