internal/socket: add message IO functionality

This change adds portable message IO methods of Conn, IO message and
control message types, and parse methods for the types to provide
those functionality to the ipv4 and ipv6 packages.

With this change, the ipv4 and ipv6 packages can provide low-level
(but less heap allocation and the cost of invoking system calls) API
such as read and write operations for a batch of IO messages.

On vanilla linux/amd64 virtual machine:
BenchmarkUDP/Iter-1-2            1000000              8068 ns/op             408 B/op         14 allocs/op
BenchmarkUDP/Batch-1-2           1000000              8610 ns/op             440 B/op         14 allocs/op
BenchmarkUDP/Iter-2-2             500000             15390 ns/op             816 B/op         28 allocs/op
BenchmarkUDP/Batch-2-2            500000             12715 ns/op             696 B/op         20 allocs/op
BenchmarkUDP/Iter-4-2             200000             30763 ns/op            1632 B/op         56 allocs/op
BenchmarkUDP/Batch-4-2            300000             21853 ns/op            1216 B/op         32 allocs/op
BenchmarkUDP/Iter-8-2             100000             61460 ns/op            3264 B/op        112 allocs/op
BenchmarkUDP/Batch-8-2            200000             39048 ns/op            2256 B/op         56 allocs/op
BenchmarkUDP/Iter-16-2             50000            122408 ns/op            6528 B/op        224 allocs/op
BenchmarkUDP/Batch-16-2           100000             72728 ns/op            4336 B/op        104 allocs/op
BenchmarkUDP/Iter-32-2             30000            243137 ns/op           13056 B/op        448 allocs/op
BenchmarkUDP/Batch-32-2            50000            141332 ns/op            8496 B/op        200 allocs/op
BenchmarkUDP/Iter-64-2             20000            488125 ns/op           26112 B/op        896 allocs/op
BenchmarkUDP/Batch-64-2            30000            282078 ns/op           16816 B/op        392 allocs/op
BenchmarkUDP/Iter-128-2            10000            973752 ns/op           52224 B/op       1792 allocs/op
BenchmarkUDP/Batch-128-2           10000            551021 ns/op           33456 B/op        776 allocs/op
BenchmarkUDP/Iter-256-2             3000           1977852 ns/op          104448 B/op       3584 allocs/op
BenchmarkUDP/Batch-256-2           10000           1252596 ns/op           66736 B/op       1544 allocs/op
BenchmarkUDP/Iter-512-2             2000           4147495 ns/op          208896 B/op       7168 allocs/op
BenchmarkUDP/Batch-512-2            3000           2175774 ns/op          121128 B/op       2612 allocs/op

Change-Id: I3e08b28917b62dbea7936a86acb24e25ccaf5365
Reviewed-on: https://go-review.googlesource.com/38212
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/internal/socket/cmsghdr.go b/internal/socket/cmsghdr.go
new file mode 100644
index 0000000..1eb07d2
--- /dev/null
+++ b/internal/socket/cmsghdr.go
@@ -0,0 +1,11 @@
+// Copyright 2017 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 solaris
+
+package socket
+
+func (h *cmsghdr) len() int { return int(h.Len) }
+func (h *cmsghdr) lvl() int { return int(h.Level) }
+func (h *cmsghdr) typ() int { return int(h.Type) }
diff --git a/internal/socket/cmsghdr_bsd.go b/internal/socket/cmsghdr_bsd.go
new file mode 100644
index 0000000..d1d0c2d
--- /dev/null
+++ b/internal/socket/cmsghdr_bsd.go
@@ -0,0 +1,13 @@
+// Copyright 2017 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 socket
+
+func (h *cmsghdr) set(l, lvl, typ int) {
+	h.Len = uint32(l)
+	h.Level = int32(lvl)
+	h.Type = int32(typ)
+}
diff --git a/internal/socket/cmsghdr_linux_32bit.go b/internal/socket/cmsghdr_linux_32bit.go
new file mode 100644
index 0000000..bac6681
--- /dev/null
+++ b/internal/socket/cmsghdr_linux_32bit.go
@@ -0,0 +1,14 @@
+// Copyright 2017 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 arm mips mipsle 386
+// +build linux
+
+package socket
+
+func (h *cmsghdr) set(l, lvl, typ int) {
+	h.Len = uint32(l)
+	h.Level = int32(lvl)
+	h.Type = int32(typ)
+}
diff --git a/internal/socket/cmsghdr_linux_64bit.go b/internal/socket/cmsghdr_linux_64bit.go
new file mode 100644
index 0000000..63f0534
--- /dev/null
+++ b/internal/socket/cmsghdr_linux_64bit.go
@@ -0,0 +1,14 @@
+// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
+// +build linux
+
+package socket
+
+func (h *cmsghdr) set(l, lvl, typ int) {
+	h.Len = uint64(l)
+	h.Level = int32(lvl)
+	h.Type = int32(typ)
+}
diff --git a/internal/socket/cmsghdr_solaris_64bit.go b/internal/socket/cmsghdr_solaris_64bit.go
new file mode 100644
index 0000000..7dedd43
--- /dev/null
+++ b/internal/socket/cmsghdr_solaris_64bit.go
@@ -0,0 +1,14 @@
+// Copyright 2017 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 amd64
+// +build solaris
+
+package socket
+
+func (h *cmsghdr) set(l, lvl, typ int) {
+	h.Len = uint32(l)
+	h.Level = int32(lvl)
+	h.Type = int32(typ)
+}
diff --git a/internal/socket/cmsghdr_stub.go b/internal/socket/cmsghdr_stub.go
new file mode 100644
index 0000000..a4e7122
--- /dev/null
+++ b/internal/socket/cmsghdr_stub.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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,!solaris
+
+package socket
+
+type cmsghdr struct{}
+
+const sizeofCmsghdr = 0
+
+func (h *cmsghdr) len() int { return 0 }
+func (h *cmsghdr) lvl() int { return 0 }
+func (h *cmsghdr) typ() int { return 0 }
+
+func (h *cmsghdr) set(l, lvl, typ int) {}
diff --git a/internal/socket/iovec_32bit.go b/internal/socket/iovec_32bit.go
new file mode 100644
index 0000000..d6a570c
--- /dev/null
+++ b/internal/socket/iovec_32bit.go
@@ -0,0 +1,15 @@
+// Copyright 2017 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 arm mips mipsle 386
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package socket
+
+import "unsafe"
+
+func (v *iovec) set(b []byte) {
+	v.Base = (*byte)(unsafe.Pointer(&b[0]))
+	v.Len = uint32(len(b))
+}
diff --git a/internal/socket/iovec_64bit.go b/internal/socket/iovec_64bit.go
new file mode 100644
index 0000000..2ae435e
--- /dev/null
+++ b/internal/socket/iovec_64bit.go
@@ -0,0 +1,15 @@
+// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package socket
+
+import "unsafe"
+
+func (v *iovec) set(b []byte) {
+	v.Base = (*byte)(unsafe.Pointer(&b[0]))
+	v.Len = uint64(len(b))
+}
diff --git a/internal/socket/iovec_solaris_64bit.go b/internal/socket/iovec_solaris_64bit.go
new file mode 100644
index 0000000..100a628
--- /dev/null
+++ b/internal/socket/iovec_solaris_64bit.go
@@ -0,0 +1,15 @@
+// Copyright 2017 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 amd64
+// +build solaris
+
+package socket
+
+import "unsafe"
+
+func (v *iovec) set(b []byte) {
+	v.Base = (*int8)(unsafe.Pointer(&b[0]))
+	v.Len = uint64(len(b))
+}
diff --git a/internal/socket/iovec_stub.go b/internal/socket/iovec_stub.go
new file mode 100644
index 0000000..c87d2a9
--- /dev/null
+++ b/internal/socket/iovec_stub.go
@@ -0,0 +1,11 @@
+// Copyright 2017 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,!solaris
+
+package socket
+
+type iovec struct{}
+
+func (v *iovec) set(b []byte) {}
diff --git a/internal/socket/mmsghdr_stub.go b/internal/socket/mmsghdr_stub.go
new file mode 100644
index 0000000..2e80a9c
--- /dev/null
+++ b/internal/socket/mmsghdr_stub.go
@@ -0,0 +1,21 @@
+// Copyright 2017 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 !linux,!netbsd
+
+package socket
+
+import "net"
+
+type mmsghdr struct{}
+
+type mmsghdrs []mmsghdr
+
+func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
+	return nil
+}
+
+func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
+	return nil
+}
diff --git a/internal/socket/mmsghdr_unix.go b/internal/socket/mmsghdr_unix.go
new file mode 100644
index 0000000..3c42ea7
--- /dev/null
+++ b/internal/socket/mmsghdr_unix.go
@@ -0,0 +1,42 @@
+// Copyright 2017 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 linux netbsd
+
+package socket
+
+import "net"
+
+type mmsghdrs []mmsghdr
+
+func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
+	for i := range hs {
+		vs := make([]iovec, len(ms[i].Buffers))
+		var sa []byte
+		if parseFn != nil {
+			sa = make([]byte, sizeofSockaddrInet6)
+		}
+		if marshalFn != nil {
+			sa = marshalFn(ms[i].Addr)
+		}
+		hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa)
+	}
+	return nil
+}
+
+func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
+	for i := range hs {
+		ms[i].N = int(hs[i].Len)
+		ms[i].NN = hs[i].Hdr.controllen()
+		ms[i].Flags = hs[i].Hdr.flags()
+		if parseFn != nil {
+			var err error
+			ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
diff --git a/internal/socket/msghdr_bsd.go b/internal/socket/msghdr_bsd.go
new file mode 100644
index 0000000..5567afc
--- /dev/null
+++ b/internal/socket/msghdr_bsd.go
@@ -0,0 +1,39 @@
+// Copyright 2017 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 socket
+
+import "unsafe"
+
+func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
+	for i := range vs {
+		vs[i].set(bs[i])
+	}
+	h.setIov(vs)
+	if len(oob) > 0 {
+		h.Control = (*byte)(unsafe.Pointer(&oob[0]))
+		h.Controllen = uint32(len(oob))
+	}
+	if sa != nil {
+		h.Name = (*byte)(unsafe.Pointer(&sa[0]))
+		h.Namelen = uint32(len(sa))
+	}
+}
+
+func (h *msghdr) name() []byte {
+	if h.Name != nil && h.Namelen > 0 {
+		return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
+	}
+	return nil
+}
+
+func (h *msghdr) controllen() int {
+	return int(h.Controllen)
+}
+
+func (h *msghdr) flags() int {
+	return int(h.Flags)
+}
diff --git a/internal/socket/msghdr_bsdvar.go b/internal/socket/msghdr_bsdvar.go
new file mode 100644
index 0000000..3fcb042
--- /dev/null
+++ b/internal/socket/msghdr_bsdvar.go
@@ -0,0 +1,12 @@
+// Copyright 2017 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
+
+package socket
+
+func (h *msghdr) setIov(vs []iovec) {
+	h.Iov = &vs[0]
+	h.Iovlen = int32(len(vs))
+}
diff --git a/internal/socket/msghdr_linux.go b/internal/socket/msghdr_linux.go
new file mode 100644
index 0000000..5a38798
--- /dev/null
+++ b/internal/socket/msghdr_linux.go
@@ -0,0 +1,36 @@
+// Copyright 2017 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 socket
+
+import "unsafe"
+
+func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
+	for i := range vs {
+		vs[i].set(bs[i])
+	}
+	h.setIov(vs)
+	if len(oob) > 0 {
+		h.setControl(oob)
+	}
+	if sa != nil {
+		h.Name = (*byte)(unsafe.Pointer(&sa[0]))
+		h.Namelen = uint32(len(sa))
+	}
+}
+
+func (h *msghdr) name() []byte {
+	if h.Name != nil && h.Namelen > 0 {
+		return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
+	}
+	return nil
+}
+
+func (h *msghdr) controllen() int {
+	return int(h.Controllen)
+}
+
+func (h *msghdr) flags() int {
+	return int(h.Flags)
+}
diff --git a/internal/socket/msghdr_linux_32bit.go b/internal/socket/msghdr_linux_32bit.go
new file mode 100644
index 0000000..9f671ae
--- /dev/null
+++ b/internal/socket/msghdr_linux_32bit.go
@@ -0,0 +1,20 @@
+// Copyright 2017 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 arm mips mipsle 386
+// +build linux
+
+package socket
+
+import "unsafe"
+
+func (h *msghdr) setIov(vs []iovec) {
+	h.Iov = &vs[0]
+	h.Iovlen = uint32(len(vs))
+}
+
+func (h *msghdr) setControl(b []byte) {
+	h.Control = (*byte)(unsafe.Pointer(&b[0]))
+	h.Controllen = uint32(len(b))
+}
diff --git a/internal/socket/msghdr_linux_64bit.go b/internal/socket/msghdr_linux_64bit.go
new file mode 100644
index 0000000..9f78706
--- /dev/null
+++ b/internal/socket/msghdr_linux_64bit.go
@@ -0,0 +1,20 @@
+// Copyright 2017 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
+// +build linux
+
+package socket
+
+import "unsafe"
+
+func (h *msghdr) setIov(vs []iovec) {
+	h.Iov = &vs[0]
+	h.Iovlen = uint64(len(vs))
+}
+
+func (h *msghdr) setControl(b []byte) {
+	h.Control = (*byte)(unsafe.Pointer(&b[0]))
+	h.Controllen = uint64(len(b))
+}
diff --git a/internal/socket/msghdr_openbsd.go b/internal/socket/msghdr_openbsd.go
new file mode 100644
index 0000000..be354ff
--- /dev/null
+++ b/internal/socket/msghdr_openbsd.go
@@ -0,0 +1,10 @@
+// Copyright 2017 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 socket
+
+func (h *msghdr) setIov(vs []iovec) {
+	h.Iov = &vs[0]
+	h.Iovlen = uint32(len(vs))
+}
diff --git a/internal/socket/msghdr_solaris_64bit.go b/internal/socket/msghdr_solaris_64bit.go
new file mode 100644
index 0000000..d1b0593
--- /dev/null
+++ b/internal/socket/msghdr_solaris_64bit.go
@@ -0,0 +1,34 @@
+// Copyright 2017 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 amd64
+// +build solaris
+
+package socket
+
+import "unsafe"
+
+func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
+	for i := range vs {
+		vs[i].set(bs[i])
+	}
+	h.Iov = &vs[0]
+	h.Iovlen = int32(len(vs))
+	if len(oob) > 0 {
+		h.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
+		h.Accrightslen = int32(len(oob))
+	}
+	if sa != nil {
+		h.Name = (*byte)(unsafe.Pointer(&sa[0]))
+		h.Namelen = uint32(len(sa))
+	}
+}
+
+func (h *msghdr) controllen() int {
+	return int(h.Accrightslen)
+}
+
+func (h *msghdr) flags() int {
+	return int(NativeEndian.Uint32(h.Pad_cgo_2[:]))
+}
diff --git a/internal/socket/msghdr_stub.go b/internal/socket/msghdr_stub.go
new file mode 100644
index 0000000..64e8173
--- /dev/null
+++ b/internal/socket/msghdr_stub.go
@@ -0,0 +1,14 @@
+// Copyright 2017 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,!solaris
+
+package socket
+
+type msghdr struct{}
+
+func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {}
+func (h *msghdr) name() []byte                                        { return nil }
+func (h *msghdr) controllen() int                                     { return 0 }
+func (h *msghdr) flags() int                                          { return 0 }
diff --git a/internal/socket/rawconn_mmsg.go b/internal/socket/rawconn_mmsg.go
new file mode 100644
index 0000000..499164a
--- /dev/null
+++ b/internal/socket/rawconn_mmsg.go
@@ -0,0 +1,74 @@
+// Copyright 2017 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 go1.9
+// +build linux
+
+package socket
+
+import (
+	"net"
+	"os"
+	"syscall"
+)
+
+func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
+	hs := make(mmsghdrs, len(ms))
+	var parseFn func([]byte, string) (net.Addr, error)
+	if c.network != "tcp" {
+		parseFn = parseInetAddr
+	}
+	if err := hs.pack(ms, parseFn, nil); err != nil {
+		return 0, err
+	}
+	var operr error
+	var n int
+	fn := func(s uintptr) bool {
+		n, operr = recvmmsg(s, hs, flags)
+		if operr == syscall.EAGAIN {
+			return false
+		}
+		return true
+	}
+	if err := c.c.Read(fn); err != nil {
+		return n, err
+	}
+	if operr != nil {
+		return n, os.NewSyscallError("recvmmsg", operr)
+	}
+	if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil {
+		return n, err
+	}
+	return n, nil
+}
+
+func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
+	hs := make(mmsghdrs, len(ms))
+	var marshalFn func(net.Addr) []byte
+	if c.network != "tcp" {
+		marshalFn = marshalInetAddr
+	}
+	if err := hs.pack(ms, nil, marshalFn); err != nil {
+		return 0, err
+	}
+	var operr error
+	var n int
+	fn := func(s uintptr) bool {
+		n, operr = sendmmsg(s, hs, flags)
+		if operr == syscall.EAGAIN {
+			return false
+		}
+		return true
+	}
+	if err := c.c.Write(fn); err != nil {
+		return n, err
+	}
+	if operr != nil {
+		return n, os.NewSyscallError("sendmmsg", operr)
+	}
+	if err := hs[:n].unpack(ms[:n], nil, ""); err != nil {
+		return n, err
+	}
+	return n, nil
+}
diff --git a/internal/socket/rawconn_msg.go b/internal/socket/rawconn_msg.go
new file mode 100644
index 0000000..b21d2e6
--- /dev/null
+++ b/internal/socket/rawconn_msg.go
@@ -0,0 +1,77 @@
+// Copyright 2017 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 go1.9
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+
+package socket
+
+import (
+	"os"
+	"syscall"
+)
+
+func (c *Conn) recvMsg(m *Message, flags int) error {
+	var h msghdr
+	vs := make([]iovec, len(m.Buffers))
+	var sa []byte
+	if c.network != "tcp" {
+		sa = make([]byte, sizeofSockaddrInet6)
+	}
+	h.pack(vs, m.Buffers, m.OOB, sa)
+	var operr error
+	var n int
+	fn := func(s uintptr) bool {
+		n, operr = recvmsg(s, &h, flags)
+		if operr == syscall.EAGAIN {
+			return false
+		}
+		return true
+	}
+	if err := c.c.Read(fn); err != nil {
+		return err
+	}
+	if operr != nil {
+		return os.NewSyscallError("recvmsg", operr)
+	}
+	if c.network != "tcp" {
+		var err error
+		m.Addr, err = parseInetAddr(sa[:], c.network)
+		if err != nil {
+			return err
+		}
+	}
+	m.N = n
+	m.NN = h.controllen()
+	m.Flags = h.flags()
+	return nil
+}
+
+func (c *Conn) sendMsg(m *Message, flags int) error {
+	var h msghdr
+	vs := make([]iovec, len(m.Buffers))
+	var sa []byte
+	if m.Addr != nil {
+		sa = marshalInetAddr(m.Addr)
+	}
+	h.pack(vs, m.Buffers, m.OOB, sa)
+	var operr error
+	var n int
+	fn := func(s uintptr) bool {
+		n, operr = sendmsg(s, &h, flags)
+		if operr == syscall.EAGAIN {
+			return false
+		}
+		return true
+	}
+	if err := c.c.Write(fn); err != nil {
+		return err
+	}
+	if operr != nil {
+		return os.NewSyscallError("sendmsg", operr)
+	}
+	m.N = n
+	m.NN = len(m.OOB)
+	return nil
+}
diff --git a/internal/socket/rawconn_nommsg.go b/internal/socket/rawconn_nommsg.go
new file mode 100644
index 0000000..f78832a
--- /dev/null
+++ b/internal/socket/rawconn_nommsg.go
@@ -0,0 +1,18 @@
+// Copyright 2017 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 go1.9
+// +build !linux
+
+package socket
+
+import "errors"
+
+func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
diff --git a/internal/socket/rawconn_nomsg.go b/internal/socket/rawconn_nomsg.go
new file mode 100644
index 0000000..96733cb
--- /dev/null
+++ b/internal/socket/rawconn_nomsg.go
@@ -0,0 +1,18 @@
+// Copyright 2017 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 go1.9
+// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+
+package socket
+
+import "errors"
+
+func (c *Conn) recvMsg(m *Message, flags int) error {
+	return errors.New("not implemented")
+}
+
+func (c *Conn) sendMsg(m *Message, flags int) error {
+	return errors.New("not implemented")
+}
diff --git a/internal/socket/rawconn_stub.go b/internal/socket/rawconn_stub.go
new file mode 100644
index 0000000..d2add1a
--- /dev/null
+++ b/internal/socket/rawconn_stub.go
@@ -0,0 +1,25 @@
+// Copyright 2017 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 !go1.9
+
+package socket
+
+import "errors"
+
+func (c *Conn) recvMsg(m *Message, flags int) error {
+	return errors.New("not implemented")
+}
+
+func (c *Conn) sendMsg(m *Message, flags int) error {
+	return errors.New("not implemented")
+}
+
+func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
diff --git a/internal/socket/socket.go b/internal/socket/socket.go
index 366b8da..1eab2fb 100644
--- a/internal/socket/socket.go
+++ b/internal/socket/socket.go
@@ -6,7 +6,11 @@
 // calls.
 package socket // import "golang.org/x/net/internal/socket"
 
-import "errors"
+import (
+	"errors"
+	"net"
+	"unsafe"
+)
 
 // An Option represents a sticky socket option.
 type Option struct {
@@ -82,3 +86,197 @@
 	}
 	return o.set(c, b)
 }
+
+func controlHeaderLen() int {
+	return roundup(sizeofCmsghdr)
+}
+
+func controlMessageLen(dataLen int) int {
+	return roundup(sizeofCmsghdr) + dataLen
+}
+
+// ControlMessageSpace returns the whole length of control message.
+func ControlMessageSpace(dataLen int) int {
+	return roundup(sizeofCmsghdr) + roundup(dataLen)
+}
+
+// A ControlMessage represents the head message in a stream of control
+// messages.
+//
+// A control message comprises of a header, data and a few padding
+// fields to conform to the interface to the kernel.
+//
+// See RFC 3542 for further information.
+type ControlMessage []byte
+
+// Data returns the data field of the control message at the head on
+// w.
+func (m ControlMessage) Data(dataLen int) []byte {
+	l := controlHeaderLen()
+	if len(m) < l || len(m) < l+dataLen {
+		return nil
+	}
+	return m[l : l+dataLen]
+}
+
+// Next returns the control message at the next on w.
+//
+// Next works only for standard control messages.
+func (m ControlMessage) Next(dataLen int) ControlMessage {
+	l := ControlMessageSpace(dataLen)
+	if len(m) < l {
+		return nil
+	}
+	return m[l:]
+}
+
+// MarshalHeader marshals the header fields of the control message at
+// the head on w.
+func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
+	if len(m) < controlHeaderLen() {
+		return errors.New("short message")
+	}
+	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
+	h.set(controlMessageLen(dataLen), lvl, typ)
+	return nil
+}
+
+// ParseHeader parses and returns the header fields of the control
+// message at the head on w.
+func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
+	l := controlHeaderLen()
+	if len(m) < l {
+		return 0, 0, 0, errors.New("short message")
+	}
+	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
+	return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
+}
+
+// Marshal marshals the control message at the head on w, and returns
+// the next control message.
+func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
+	l := len(data)
+	if len(m) < ControlMessageSpace(l) {
+		return nil, errors.New("short message")
+	}
+	h := (*cmsghdr)(unsafe.Pointer(&m[0]))
+	h.set(controlMessageLen(l), lvl, typ)
+	if l > 0 {
+		copy(m.Data(l), data)
+	}
+	return m.Next(l), nil
+}
+
+// Parse parses w as a single or multiple control messages.
+//
+// Parse works for both standard and compatible messages.
+func (m ControlMessage) Parse() ([]ControlMessage, error) {
+	var ms []ControlMessage
+	for len(m) >= controlHeaderLen() {
+		h := (*cmsghdr)(unsafe.Pointer(&m[0]))
+		l := h.len()
+		if uint64(l) < uint64(controlHeaderLen()) {
+			return nil, errors.New("invalid message length")
+		}
+		if uint64(l) > uint64(len(m)) {
+			return nil, errors.New("short buffer")
+		}
+		// On message reception:
+		//
+		// |<- ControlMessageSpace --------------->|
+		// |<- controlMessageLen ---------->|      |
+		// |<- controlHeaderLen ->|         |      |
+		// +---------------+------+---------+------+
+		// |    Header     | PadH |  Data   | PadD |
+		// +---------------+------+---------+------+
+		//
+		// On compatible message reception:
+		//
+		// | ... |<- controlMessageLen ----------->|
+		// | ... |<- controlHeaderLen ->|          |
+		// +-----+---------------+------+----------+
+		// | ... |    Header     | PadH |   Data   |
+		// +-----+---------------+------+----------+
+		ms = append(ms, ControlMessage(m[:l]))
+		ll := l - controlHeaderLen()
+		if len(m) >= ControlMessageSpace(ll) {
+			m = m[ControlMessageSpace(ll):]
+		} else {
+			m = m[controlMessageLen(ll):]
+		}
+	}
+	return ms, nil
+}
+
+// NewControlMessage returns a new stream of control messages.
+func NewControlMessage(dataLen []int) ControlMessage {
+	var l int
+	for i := range dataLen {
+		l += ControlMessageSpace(dataLen[i])
+	}
+	return make([]byte, l)
+}
+
+// A Message represents an IO message.
+type Message struct {
+	// When writing, the Buffers field must contain at least one
+	// byte to write.
+	// When reading, the Buffers field will always contain a byte
+	// to read.
+	Buffers [][]byte
+
+	// OOB contains protocol-specific control or miscellaneous
+	// ancillary data known as out-of-band data.
+	OOB []byte
+
+	// Addr specifies a destination address when writing.
+	// It can be nil when the underlying protocol of the raw
+	// connection uses connection-oriented communication.
+	// After a successful read, it may contain the source address
+	// on the received packet.
+	Addr net.Addr
+
+	N     int // # of bytes read or written from/to Buffers
+	NN    int // # of bytes read or written from/to OOB
+	Flags int // protocol-specific information on the received message
+}
+
+// RecvMsg wraps recvmsg system call.
+//
+// The provided flags is a set of platform-dependent flags, such as
+// syscall.MSG_PEEK.
+func (c *Conn) RecvMsg(m *Message, flags int) error {
+	return c.recvMsg(m, flags)
+}
+
+// SendMsg wraps sendmsg system call.
+//
+// The provided flags is a set of platform-dependent flags, such as
+// syscall.MSG_DONTROUTE.
+func (c *Conn) SendMsg(m *Message, flags int) error {
+	return c.sendMsg(m, flags)
+}
+
+// RecvMsgs wraps recvmmsg system call.
+//
+// It returns the number of processed messages.
+//
+// The provided flags is a set of platform-dependent flags, such as
+// syscall.MSG_PEEK.
+//
+// Only Linux supports this.
+func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) {
+	return c.recvMsgs(ms, flags)
+}
+
+// SendMsgs wraps sendmmsg system call.
+//
+// It returns the number of processed messages.
+//
+// The provided flags is a set of platform-dependent flags, such as
+// syscall.MSG_DONTROUTE.
+//
+// Only Linux supports this.
+func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) {
+	return c.sendMsgs(ms, flags)
+}
diff --git a/internal/socket/socket_go1_9_test.go b/internal/socket/socket_go1_9_test.go
new file mode 100644
index 0000000..109fed7
--- /dev/null
+++ b/internal/socket/socket_go1_9_test.go
@@ -0,0 +1,256 @@
+// Copyright 2017 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 go1.9
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package socket_test
+
+import (
+	"bytes"
+	"fmt"
+	"net"
+	"runtime"
+	"testing"
+
+	"golang.org/x/net/internal/nettest"
+	"golang.org/x/net/internal/socket"
+)
+
+type mockControl struct {
+	Level int
+	Type  int
+	Data  []byte
+}
+
+func TestControlMessage(t *testing.T) {
+	for _, tt := range []struct {
+		cs []mockControl
+	}{
+		{
+			[]mockControl{
+				{Level: 1, Type: 1},
+			},
+		},
+		{
+			[]mockControl{
+				{Level: 2, Type: 2, Data: []byte{0xfe}},
+			},
+		},
+		{
+			[]mockControl{
+				{Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}},
+			},
+		},
+		{
+			[]mockControl{
+				{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
+			},
+		},
+		{
+			[]mockControl{
+				{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
+				{Level: 2, Type: 2, Data: []byte{0xfe}},
+			},
+		},
+	} {
+		var w []byte
+		var tailPadLen int
+		mm := socket.NewControlMessage([]int{0})
+		for i, c := range tt.cs {
+			m := socket.NewControlMessage([]int{len(c.Data)})
+			l := len(m) - len(mm)
+			if i == len(tt.cs)-1 && l > len(c.Data) {
+				tailPadLen = l - len(c.Data)
+			}
+			w = append(w, m...)
+		}
+
+		var err error
+		ww := make([]byte, len(w))
+		copy(ww, w)
+		m := socket.ControlMessage(ww)
+		for _, c := range tt.cs {
+			if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil {
+				t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err)
+			}
+			copy(m.Data(len(c.Data)), c.Data)
+			m = m.Next(len(c.Data))
+		}
+		m = socket.ControlMessage(w)
+		for _, c := range tt.cs {
+			m, err = m.Marshal(c.Level, c.Type, c.Data)
+			if err != nil {
+				t.Fatalf("(%v).Marshal() = %v", tt.cs, err)
+			}
+		}
+		if !bytes.Equal(ww, w) {
+			t.Fatalf("got %#v; want %#v", ww, w)
+		}
+
+		ws := [][]byte{w}
+		if tailPadLen > 0 {
+			// Test a message with no tail padding.
+			nopad := w[:len(w)-tailPadLen]
+			ws = append(ws, [][]byte{nopad}...)
+		}
+		for _, w := range ws {
+			ms, err := socket.ControlMessage(w).Parse()
+			if err != nil {
+				t.Fatalf("(%v).Parse() = %v", tt.cs, err)
+			}
+			for i, m := range ms {
+				lvl, typ, dataLen, err := m.ParseHeader()
+				if err != nil {
+					t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err)
+				}
+				if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) {
+					t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data))
+				}
+			}
+		}
+	}
+}
+
+func TestUDP(t *testing.T) {
+	c, err := nettest.NewLocalPacketListener("udp")
+	if err != nil {
+		t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+	}
+	defer c.Close()
+
+	t.Run("Message", func(t *testing.T) {
+		testUDPMessage(t, c.(net.Conn))
+	})
+	switch runtime.GOOS {
+	case "linux":
+		t.Run("Messages", func(t *testing.T) {
+			testUDPMessages(t, c.(net.Conn))
+		})
+	}
+}
+
+func testUDPMessage(t *testing.T, c net.Conn) {
+	cc, err := socket.NewConn(c)
+	if err != nil {
+		t.Fatal(err)
+	}
+	data := []byte("HELLO-R-U-THERE")
+	wm := socket.Message{
+		Buffers: bytes.SplitAfter(data, []byte("-")),
+		Addr:    c.LocalAddr(),
+	}
+	if err := cc.SendMsg(&wm, 0); err != nil {
+		t.Fatal(err)
+	}
+	b := make([]byte, 32)
+	rm := socket.Message{
+		Buffers: [][]byte{b[:1], b[1:3], b[3:7], b[7:11], b[11:]},
+	}
+	if err := cc.RecvMsg(&rm, 0); err != nil {
+		t.Fatal(err)
+	}
+	if !bytes.Equal(b[:rm.N], data) {
+		t.Fatalf("got %#v; want %#v", b[:rm.N], data)
+	}
+}
+
+func testUDPMessages(t *testing.T, c net.Conn) {
+	cc, err := socket.NewConn(c)
+	if err != nil {
+		t.Fatal(err)
+	}
+	data := []byte("HELLO-R-U-THERE")
+	wmbs := bytes.SplitAfter(data, []byte("-"))
+	wms := []socket.Message{
+		{Buffers: wmbs[:1], Addr: c.LocalAddr()},
+		{Buffers: wmbs[1:], Addr: c.LocalAddr()},
+	}
+	n, err := cc.SendMsgs(wms, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if n != len(wms) {
+		t.Fatalf("got %d; want %d", n, len(wms))
+	}
+	b := make([]byte, 32)
+	rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}}
+	rms := []socket.Message{
+		{Buffers: rmbs[0]},
+		{Buffers: rmbs[1]},
+	}
+	n, err = cc.RecvMsgs(rms, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if n != len(rms) {
+		t.Fatalf("got %d; want %d", n, len(rms))
+	}
+	nn := 0
+	for i := 0; i < n; i++ {
+		nn += rms[i].N
+	}
+	if !bytes.Equal(b[:nn], data) {
+		t.Fatalf("got %#v; want %#v", b[:nn], data)
+	}
+}
+
+func BenchmarkUDP(b *testing.B) {
+	c, err := nettest.NewLocalPacketListener("udp")
+	if err != nil {
+		b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+	}
+	defer c.Close()
+	cc, err := socket.NewConn(c.(net.Conn))
+	if err != nil {
+		b.Fatal(err)
+	}
+	data := []byte("HELLO-R-U-THERE")
+	wm := socket.Message{
+		Buffers: [][]byte{data},
+		Addr:    c.LocalAddr(),
+	}
+	rm := socket.Message{
+		Buffers: [][]byte{make([]byte, 128)},
+		OOB:     make([]byte, 128),
+	}
+
+	for M := 1; M <= 1<<9; M = M << 1 {
+		b.Run(fmt.Sprintf("Iter-%d", M), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				for j := 0; j < M; j++ {
+					if err := cc.SendMsg(&wm, 0); err != nil {
+						b.Fatal(err)
+					}
+					if err := cc.RecvMsg(&rm, 0); err != nil {
+						b.Fatal(err)
+					}
+				}
+			}
+		})
+		switch runtime.GOOS {
+		case "linux":
+			wms := make([]socket.Message, M)
+			for i := range wms {
+				wms[i].Buffers = [][]byte{data}
+				wms[i].Addr = c.LocalAddr()
+			}
+			rms := make([]socket.Message, M)
+			for i := range rms {
+				rms[i].Buffers = [][]byte{make([]byte, 128)}
+				rms[i].OOB = make([]byte, 128)
+			}
+			b.Run(fmt.Sprintf("Batch-%d", M), func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					if _, err := cc.SendMsgs(wms, 0); err != nil {
+						b.Fatal(err)
+					}
+					if _, err := cc.RecvMsgs(rms, 0); err != nil {
+						b.Fatal(err)
+					}
+				}
+			})
+		}
+	}
+}
diff --git a/internal/socket/sys.go b/internal/socket/sys.go
index 51702b8..4f0eead 100644
--- a/internal/socket/sys.go
+++ b/internal/socket/sys.go
@@ -9,9 +9,13 @@
 	"unsafe"
 )
 
-// NativeEndian is the machine native endian implementation of
-// ByteOrder.
-var NativeEndian binary.ByteOrder
+var (
+	// NativeEndian is the machine native endian implementation of
+	// ByteOrder.
+	NativeEndian binary.ByteOrder
+
+	kernelAlign int
+)
 
 func init() {
 	i := uint32(1)
@@ -21,4 +25,9 @@
 	} else {
 		NativeEndian = binary.BigEndian
 	}
+	kernelAlign = probeProtocolStack()
+}
+
+func roundup(l int) int {
+	return (l + kernelAlign - 1) & ^(kernelAlign - 1)
 }
diff --git a/internal/socket/sys_bsd.go b/internal/socket/sys_bsd.go
new file mode 100644
index 0000000..f13e14f
--- /dev/null
+++ b/internal/socket/sys_bsd.go
@@ -0,0 +1,17 @@
+// Copyright 2017 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 openbsd
+
+package socket
+
+import "errors"
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
diff --git a/internal/socket/sys_bsdvar.go b/internal/socket/sys_bsdvar.go
new file mode 100644
index 0000000..eb89a60
--- /dev/null
+++ b/internal/socket/sys_bsdvar.go
@@ -0,0 +1,14 @@
+// Copyright 2017 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 netbsd openbsd
+
+package socket
+
+import "unsafe"
+
+func probeProtocolStack() int {
+	var p uintptr
+	return int(unsafe.Sizeof(p))
+}
diff --git a/internal/socket/sys_darwin.go b/internal/socket/sys_darwin.go
new file mode 100644
index 0000000..b17d223
--- /dev/null
+++ b/internal/socket/sys_darwin.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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 socket
+
+func probeProtocolStack() int { return 4 }
diff --git a/internal/socket/sys_dragonfly.go b/internal/socket/sys_dragonfly.go
new file mode 100644
index 0000000..b17d223
--- /dev/null
+++ b/internal/socket/sys_dragonfly.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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 socket
+
+func probeProtocolStack() int { return 4 }
diff --git a/internal/socket/sys_freebsd.go b/internal/socket/sys_freebsd.go
new file mode 100644
index 0000000..0f8a5b6
--- /dev/null
+++ b/internal/socket/sys_freebsd.go
@@ -0,0 +1,41 @@
+// Copyright 2017 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 socket
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+func probeProtocolStack() int {
+	var p uintptr
+	align := int(unsafe.Sizeof(p))
+	// In the case of kern.supported_archs="amd64 i386", we need
+	// to know the underlying kernel's architecture because the
+	// alignment for socket facilities are set at the build time
+	// of the kernel.
+	conf, _ := syscall.Sysctl("kern.conftxt")
+	for i, j := 0, 0; j < len(conf); j++ {
+		if conf[j] != '\n' {
+			continue
+		}
+		s := conf[i:j]
+		i = j + 1
+		if len(s) > len("machine") && s[:len("machine")] == "machine" {
+			s = s[len("machine"):]
+			for k := 0; k < len(s); k++ {
+				if s[k] == ' ' || s[k] == '\t' {
+					s = s[1:]
+				}
+				break
+			}
+			if s == "amd64" {
+				align = 8
+			}
+			break
+		}
+	}
+	return align
+}
diff --git a/internal/socket/sys_linux.go b/internal/socket/sys_linux.go
new file mode 100644
index 0000000..1559521
--- /dev/null
+++ b/internal/socket/sys_linux.go
@@ -0,0 +1,27 @@
+// Copyright 2017 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 linux,!s390x,!386
+
+package socket
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+func probeProtocolStack() int {
+	var p uintptr
+	return int(unsafe.Sizeof(p))
+}
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
diff --git a/internal/socket/sys_linux_386.go b/internal/socket/sys_linux_386.go
index 0a9ec1a..235b2cc 100644
--- a/internal/socket/sys_linux_386.go
+++ b/internal/socket/sys_linux_386.go
@@ -9,9 +9,15 @@
 	"unsafe"
 )
 
+func probeProtocolStack() int { return 4 }
+
 const (
 	sysSETSOCKOPT = 0xe
 	sysGETSOCKOPT = 0xf
+	sysSENDMSG    = 0x10
+	sysRECVMSG    = 0x11
+	sysRECVMMSG   = 0x13
+	sysSENDMMSG   = 0x14
 )
 
 func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
@@ -27,3 +33,23 @@
 	_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
 	return errnoErr(errno)
 }
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
diff --git a/internal/socket/sys_linux_s390x.go b/internal/socket/sys_linux_s390x.go
index 1f7165e..327979e 100644
--- a/internal/socket/sys_linux_s390x.go
+++ b/internal/socket/sys_linux_s390x.go
@@ -4,7 +4,52 @@
 
 package socket
 
-const (
-	sysRECVMMSG = 0x165
-	sysSENDMMSG = 0x166
+import (
+	"syscall"
+	"unsafe"
 )
+
+func probeProtocolStack() int { return 8 }
+
+const (
+	sysSETSOCKOPT = 0xe
+	sysGETSOCKOPT = 0xf
+	sysSENDMSG    = 0x10
+	sysRECVMSG    = 0x11
+	sysRECVMMSG   = 0x13
+	sysSENDMMSG   = 0x14
+)
+
+func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
+func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
+
+func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
+	l := uint32(len(b))
+	_, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
+	return int(l), errnoErr(errno)
+}
+
+func setsockopt(s uintptr, level, name int, b []byte) error {
+	_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
+	return errnoErr(errno)
+}
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
diff --git a/internal/socket/sys_linux_s390x.s b/internal/socket/sys_linux_s390x.s
new file mode 100644
index 0000000..06d7562
--- /dev/null
+++ b/internal/socket/sys_linux_s390x.s
@@ -0,0 +1,11 @@
+// Copyright 2017 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.
+
+#include "textflag.h"
+
+TEXT	·socketcall(SB),NOSPLIT,$0-72
+	JMP	syscall·socketcall(SB)
+
+TEXT	·rawsocketcall(SB),NOSPLIT,$0-72
+	JMP	syscall·rawsocketcall(SB)
diff --git a/internal/socket/sys_netbsd.go b/internal/socket/sys_netbsd.go
new file mode 100644
index 0000000..431851c
--- /dev/null
+++ b/internal/socket/sys_netbsd.go
@@ -0,0 +1,25 @@
+// Copyright 2017 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 socket
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+const (
+	sysRECVMMSG = 0x1db
+	sysSENDMMSG = 0x1dc
+)
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
+	return int(n), errnoErr(errno)
+}
diff --git a/internal/socket/sys_posix.go b/internal/socket/sys_posix.go
new file mode 100644
index 0000000..9a0dbcf
--- /dev/null
+++ b/internal/socket/sys_posix.go
@@ -0,0 +1,168 @@
+// Copyright 2017 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 go1.9
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+
+package socket
+
+import (
+	"encoding/binary"
+	"errors"
+	"net"
+	"runtime"
+	"strconv"
+	"sync"
+	"time"
+)
+
+func marshalInetAddr(a net.Addr) []byte {
+	switch a := a.(type) {
+	case *net.TCPAddr:
+		return marshalSockaddr(a.IP, a.Port, a.Zone)
+	case *net.UDPAddr:
+		return marshalSockaddr(a.IP, a.Port, a.Zone)
+	case *net.IPAddr:
+		return marshalSockaddr(a.IP, 0, a.Zone)
+	default:
+		return nil
+	}
+}
+
+func marshalSockaddr(ip net.IP, port int, zone string) []byte {
+	if ip4 := ip.To4(); ip4 != nil {
+		b := make([]byte, sizeofSockaddrInet)
+		switch runtime.GOOS {
+		case "linux", "solaris", "windows":
+			NativeEndian.PutUint16(b[:2], uint16(sysAF_INET))
+		default:
+			b[0] = sizeofSockaddrInet
+			b[1] = sysAF_INET
+		}
+		binary.BigEndian.PutUint16(b[2:4], uint16(port))
+		copy(b[4:8], ip4)
+		return b
+	}
+	if ip6 := ip.To16(); ip6 != nil && ip.To4() == nil {
+		b := make([]byte, sizeofSockaddrInet6)
+		switch runtime.GOOS {
+		case "linux", "solaris", "windows":
+			NativeEndian.PutUint16(b[:2], uint16(sysAF_INET6))
+		default:
+			b[0] = sizeofSockaddrInet6
+			b[1] = sysAF_INET6
+		}
+		binary.BigEndian.PutUint16(b[2:4], uint16(port))
+		copy(b[8:24], ip6)
+		if zone != "" {
+			NativeEndian.PutUint32(b[24:28], uint32(zoneCache.index(zone)))
+		}
+		return b
+	}
+	return nil
+}
+
+func parseInetAddr(b []byte, network string) (net.Addr, error) {
+	if len(b) < 2 {
+		return nil, errors.New("invalid address")
+	}
+	var af int
+	switch runtime.GOOS {
+	case "linux", "solaris", "windows":
+		af = int(NativeEndian.Uint16(b[:2]))
+	default:
+		af = int(b[1])
+	}
+	var ip net.IP
+	var zone string
+	if af == sysAF_INET {
+		if len(b) < sizeofSockaddrInet {
+			return nil, errors.New("short address")
+		}
+		ip = make(net.IP, net.IPv4len)
+		copy(ip, b[4:8])
+	}
+	if af == sysAF_INET6 {
+		if len(b) < sizeofSockaddrInet6 {
+			return nil, errors.New("short address")
+		}
+		ip = make(net.IP, net.IPv6len)
+		copy(ip, b[8:24])
+		if id := int(NativeEndian.Uint32(b[24:28])); id > 0 {
+			zone = zoneCache.name(id)
+		}
+	}
+	switch network {
+	case "tcp", "tcp4", "tcp6":
+		return &net.TCPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[2:4])), Zone: zone}, nil
+	case "udp", "udp4", "udp6":
+		return &net.UDPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[2:4])), Zone: zone}, nil
+	default:
+		return &net.IPAddr{IP: ip, Zone: zone}, nil
+	}
+}
+
+// An ipv6ZoneCache represents a cache holding partial network
+// interface information. It is used for reducing the cost of IPv6
+// addressing scope zone resolution.
+//
+// Multiple names sharing the index are managed by first-come
+// first-served basis for consistency.
+type ipv6ZoneCache struct {
+	sync.RWMutex                // guard the following
+	lastFetched  time.Time      // last time routing information was fetched
+	toIndex      map[string]int // interface name to its index
+	toName       map[int]string // interface index to its name
+}
+
+var zoneCache = ipv6ZoneCache{
+	toIndex: make(map[string]int),
+	toName:  make(map[int]string),
+}
+
+func (zc *ipv6ZoneCache) update(ift []net.Interface) {
+	zc.Lock()
+	defer zc.Unlock()
+	now := time.Now()
+	if zc.lastFetched.After(now.Add(-60 * time.Second)) {
+		return
+	}
+	zc.lastFetched = now
+	if len(ift) == 0 {
+		var err error
+		if ift, err = net.Interfaces(); err != nil {
+			return
+		}
+	}
+	zc.toIndex = make(map[string]int, len(ift))
+	zc.toName = make(map[int]string, len(ift))
+	for _, ifi := range ift {
+		zc.toIndex[ifi.Name] = ifi.Index
+		if _, ok := zc.toName[ifi.Index]; !ok {
+			zc.toName[ifi.Index] = ifi.Name
+		}
+	}
+}
+
+func (zc *ipv6ZoneCache) name(zone int) string {
+	zoneCache.update(nil)
+	zoneCache.RLock()
+	defer zoneCache.RUnlock()
+	name, ok := zoneCache.toName[zone]
+	if !ok {
+		name = strconv.Itoa(zone)
+	}
+	return name
+}
+
+func (zc *ipv6ZoneCache) index(zone string) int {
+	zoneCache.update(nil)
+	zoneCache.RLock()
+	defer zoneCache.RUnlock()
+	index, ok := zoneCache.toIndex[zone]
+	if !ok {
+		index, _ = strconv.Atoi(zone)
+	}
+	return index
+}
diff --git a/internal/socket/sys_solaris.go b/internal/socket/sys_solaris.go
index 707adaa..cced74e 100644
--- a/internal/socket/sys_solaris.go
+++ b/internal/socket/sys_solaris.go
@@ -5,19 +5,37 @@
 package socket
 
 import (
+	"errors"
+	"runtime"
 	"syscall"
 	"unsafe"
 )
 
+func probeProtocolStack() int {
+	switch runtime.GOARCH {
+	case "amd64":
+		return 4
+	default:
+		var p uintptr
+		return int(unsafe.Sizeof(p))
+	}
+}
+
 //go:cgo_import_dynamic libc___xnet_getsockopt __xnet_getsockopt "libsocket.so"
 //go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_recvmsg __xnet_recvmsg "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_sendmsg __xnet_sendmsg "libsocket.so"
 
 //go:linkname procGetsockopt libc___xnet_getsockopt
 //go:linkname procSetsockopt libc_setsockopt
+//go:linkname procRecvmsg libc___xnet_recvmsg
+//go:linkname procSendmsg libc___xnet_sendmsg
 
 var (
 	procGetsockopt uintptr
 	procSetsockopt uintptr
+	procRecvmsg    uintptr
+	procSendmsg    uintptr
 )
 
 func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno)
@@ -33,3 +51,21 @@
 	_, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procSetsockopt)), 5, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
 	return errnoErr(errno)
 }
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procRecvmsg)), 3, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procSendmsg)), 3, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
+	return int(n), errnoErr(errno)
+}
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
diff --git a/internal/socket/sys_stub.go b/internal/socket/sys_stub.go
index c3a8e13..d9f06d0 100644
--- a/internal/socket/sys_stub.go
+++ b/internal/socket/sys_stub.go
@@ -2,11 +2,42 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build nacl plan9
+// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
 
 package socket
 
-import "errors"
+import (
+	"errors"
+	"net"
+	"runtime"
+	"unsafe"
+)
+
+const (
+	sysAF_UNSPEC = 0x0
+	sysAF_INET   = 0x2
+	sysAF_INET6  = 0xa
+
+	sysSOCK_RAW = 0x3
+)
+
+func probeProtocolStack() int {
+	switch runtime.GOARCH {
+	case "amd64p32", "mips64p32":
+		return 4
+	default:
+		var p uintptr
+		return int(unsafe.Sizeof(p))
+	}
+}
+
+func marshalInetAddr(ip net.IP, port int, zone string) []byte {
+	return nil
+}
+
+func parseInetAddr(b []byte, network string) (net.Addr, error) {
+	return nil, errors.New("not implemented")
+}
 
 func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
 	return 0, errors.New("not implemented")
@@ -15,3 +46,19 @@
 func setsockopt(s uintptr, level, name int, b []byte) error {
 	return errors.New("not implemented")
 }
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
diff --git a/internal/socket/sys_unix.go b/internal/socket/sys_unix.go
index c94595e..18eba30 100644
--- a/internal/socket/sys_unix.go
+++ b/internal/socket/sys_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 dragonfly freebsd linux,!386 netbsd openbsd
+// +build darwin dragonfly freebsd linux,!s390x,!386 netbsd openbsd
 
 package socket
 
@@ -21,3 +21,13 @@
 	_, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
 	return errnoErr(errno)
 }
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, _, errno := syscall.Syscall(syscall.SYS_RECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags))
+	return int(n), errnoErr(errno)
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags))
+	return int(n), errnoErr(errno)
+}
diff --git a/internal/socket/sys_windows.go b/internal/socket/sys_windows.go
index bd3bce6..54a470e 100644
--- a/internal/socket/sys_windows.go
+++ b/internal/socket/sys_windows.go
@@ -5,10 +5,44 @@
 package socket
 
 import (
+	"errors"
 	"syscall"
 	"unsafe"
 )
 
+func probeProtocolStack() int {
+	var p uintptr
+	return int(unsafe.Sizeof(p))
+}
+
+const (
+	sysAF_UNSPEC = 0x0
+	sysAF_INET   = 0x2
+	sysAF_INET6  = 0x17
+
+	sysSOCK_RAW = 0x3
+)
+
+type sockaddrInet struct {
+	Family uint16
+	Port   uint16
+	Addr   [4]byte /* in_addr */
+	Zero   [8]uint8
+}
+
+type sockaddrInet6 struct {
+	Family   uint16
+	Port     uint16
+	Flowinfo uint32
+	Addr     [16]byte /* in6_addr */
+	Scope_id uint32
+}
+
+const (
+	sizeofSockaddrInet  = 0x10
+	sizeofSockaddrInet6 = 0x1c
+)
+
 func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
 	l := uint32(len(b))
 	err := syscall.Getsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), (*int32)(unsafe.Pointer(&l)))
@@ -18,3 +52,19 @@
 func setsockopt(s uintptr, level, name int, b []byte) error {
 	return syscall.Setsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), int32(len(b)))
 }
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}
+
+func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
+	return 0, errors.New("not implemented")
+}