ipv6: drop support for go1.8 or below

Change-Id: I9f4108283333eb56aad429bb6338231459bc2bbf
Reviewed-on: https://go-review.googlesource.com/c/net/+/162599
Run-TryBot: Mikio Hara <mikioh.public.networking@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Dave Cheney <dave@cheney.net>
diff --git a/ipv6/batch.go b/ipv6/batch.go
index 10d6492..2ccb984 100644
--- a/ipv6/batch.go
+++ b/ipv6/batch.go
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build go1.9
-
 package ipv6
 
 import (
diff --git a/ipv6/payload_cmsg.go b/ipv6/payload_cmsg.go
index e17847d..2c53f6a 100644
--- a/ipv6/payload_cmsg.go
+++ b/ipv6/payload_cmsg.go
@@ -6,7 +6,11 @@
 
 package ipv6
 
-import "net"
+import (
+	"net"
+
+	"golang.org/x/net/internal/socket"
+)
 
 // ReadFrom reads a payload of the received IPv6 datagram, from the
 // endpoint c, copying the payload into b. It returns the number of
@@ -16,7 +20,32 @@
 	if !c.ok() {
 		return 0, nil, nil, errInvalidConn
 	}
-	return c.readFrom(b)
+	c.rawOpt.RLock()
+	m := socket.Message{
+		Buffers: [][]byte{b},
+		OOB:     NewControlMessage(c.rawOpt.cflags),
+	}
+	c.rawOpt.RUnlock()
+	switch c.PacketConn.(type) {
+	case *net.UDPConn:
+		if err := c.RecvMsg(&m, 0); err != nil {
+			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
+		}
+	case *net.IPConn:
+		if err := c.RecvMsg(&m, 0); err != nil {
+			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
+		}
+	default:
+		return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: errInvalidConnType}
+	}
+	if m.NN > 0 {
+		cm = new(ControlMessage)
+		if err := cm.Parse(m.OOB[:m.NN]); err != nil {
+			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
+		}
+		cm.Src = netAddrToIP16(m.Addr)
+	}
+	return m.N, cm, m.Addr, nil
 }
 
 // WriteTo writes a payload of the IPv6 datagram, to the destination
@@ -28,5 +57,14 @@
 	if !c.ok() {
 		return 0, errInvalidConn
 	}
-	return c.writeTo(b, cm, dst)
+	m := socket.Message{
+		Buffers: [][]byte{b},
+		OOB:     cm.Marshal(),
+		Addr:    dst,
+	}
+	err = c.SendMsg(&m, 0)
+	if err != nil {
+		err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Addr: opAddr(dst), Err: err}
+	}
+	return m.N, err
 }
diff --git a/ipv6/payload_cmsg_go1_8.go b/ipv6/payload_cmsg_go1_8.go
deleted file mode 100644
index a48a6ed..0000000
--- a/ipv6/payload_cmsg_go1_8.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2013 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 ipv6
-
-import "net"
-
-func (c *payloadHandler) readFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
-	c.rawOpt.RLock()
-	oob := NewControlMessage(c.rawOpt.cflags)
-	c.rawOpt.RUnlock()
-	var nn int
-	switch c := c.PacketConn.(type) {
-	case *net.UDPConn:
-		if n, nn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
-			return 0, nil, nil, err
-		}
-	case *net.IPConn:
-		if n, nn, _, src, err = c.ReadMsgIP(b, oob); err != nil {
-			return 0, nil, nil, err
-		}
-	default:
-		return 0, nil, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Err: errInvalidConnType}
-	}
-	if nn > 0 {
-		cm = new(ControlMessage)
-		if err = cm.Parse(oob[:nn]); err != nil {
-			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
-		}
-	}
-	if cm != nil {
-		cm.Src = netAddrToIP16(src)
-	}
-	return
-}
-
-func (c *payloadHandler) writeTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
-	oob := cm.Marshal()
-	if dst == nil {
-		return 0, &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: 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, &net.OpError{Op: "write", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Addr: opAddr(dst), Err: errInvalidConnType}
-	}
-	return
-}
diff --git a/ipv6/payload_cmsg_go1_9.go b/ipv6/payload_cmsg_go1_9.go
deleted file mode 100644
index fb196ed..0000000
--- a/ipv6/payload_cmsg_go1_9.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 ipv6
-
-import (
-	"net"
-
-	"golang.org/x/net/internal/socket"
-)
-
-func (c *payloadHandler) readFrom(b []byte) (int, *ControlMessage, net.Addr, error) {
-	c.rawOpt.RLock()
-	m := socket.Message{
-		Buffers: [][]byte{b},
-		OOB:     NewControlMessage(c.rawOpt.cflags),
-	}
-	c.rawOpt.RUnlock()
-	switch c.PacketConn.(type) {
-	case *net.UDPConn:
-		if err := c.RecvMsg(&m, 0); err != nil {
-			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
-		}
-	case *net.IPConn:
-		if err := c.RecvMsg(&m, 0); err != nil {
-			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
-		}
-	default:
-		return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: errInvalidConnType}
-	}
-	var cm *ControlMessage
-	if m.NN > 0 {
-		cm = new(ControlMessage)
-		if err := cm.Parse(m.OOB[:m.NN]); err != nil {
-			return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err}
-		}
-		cm.Src = netAddrToIP16(m.Addr)
-	}
-	return m.N, cm, m.Addr, nil
-}
-
-func (c *payloadHandler) writeTo(b []byte, cm *ControlMessage, dst net.Addr) (int, error) {
-	m := socket.Message{
-		Buffers: [][]byte{b},
-		OOB:     cm.Marshal(),
-		Addr:    dst,
-	}
-	err := c.SendMsg(&m, 0)
-	if err != nil {
-		err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Addr: opAddr(dst), Err: err}
-	}
-	return m.N, err
-}
diff --git a/ipv6/readwrite_go1_8_test.go b/ipv6/readwrite_go1_8_test.go
deleted file mode 100644
index b3fbda1..0000000
--- a/ipv6/readwrite_go1_8_test.go
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2013 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 ipv6_test
-
-import (
-	"bytes"
-	"fmt"
-	"net"
-	"runtime"
-	"strings"
-	"sync"
-	"testing"
-
-	"golang.org/x/net/internal/iana"
-	"golang.org/x/net/internal/nettest"
-	"golang.org/x/net/ipv6"
-)
-
-func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
-	switch runtime.GOOS {
-	case "js", "nacl", "plan9", "windows":
-		b.Skipf("not supported on %s", runtime.GOOS)
-	}
-
-	payload := []byte("HELLO-R-U-THERE")
-	iph := []byte{
-		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	}
-	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-	bb := make([]byte, 128)
-	cm := ipv6.ControlMessage{
-		TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
-		HopLimit:     1,
-		Src:          net.IPv6loopback,
-	}
-	if ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback); ifi != nil {
-		cm.IfIndex = ifi.Index
-	}
-
-	b.Run("UDP", func(b *testing.B) {
-		c, err := nettest.NewLocalPacketListener("udp6")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv6.FlagHopLimit | ipv6.FlagInterface
-		if err := p.SetControlMessage(cf, true); err != nil {
-			b.Fatal(err)
-		}
-		b.Run("Net", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := c.WriteTo(payload, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, err := c.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-		b.Run("ToFrom", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := p.WriteTo(payload, &cm, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, _, err := p.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-	})
-	b.Run("IP", func(b *testing.B) {
-		switch runtime.GOOS {
-		case "netbsd":
-			b.Skip("need to configure gre on netbsd")
-		case "openbsd":
-			b.Skip("net.inet.gre.allow=0 by default on openbsd")
-		}
-
-		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
-		if err := p.SetControlMessage(cf, true); err != nil {
-			b.Fatal(err)
-		}
-		b.Run("Net", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := c.WriteTo(datagram, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, err := c.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-		b.Run("ToFrom", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := p.WriteTo(datagram, &cm, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, _, err := p.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-	})
-}
-
-func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
-	switch runtime.GOOS {
-	case "js", "nacl", "plan9", "windows":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
-	payload := []byte("HELLO-R-U-THERE")
-	iph := []byte{
-		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	}
-	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-
-	t.Run("UDP", func(t *testing.T) {
-		c, err := nettest.NewLocalPacketListener("udp6")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		t.Run("ToFrom", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr())
-		})
-	})
-	t.Run("IP", func(t *testing.T) {
-		switch runtime.GOOS {
-		case "netbsd":
-			t.Skip("need to configure gre on netbsd")
-		case "openbsd":
-			t.Skip("net.inet.gre.allow=0 by default on openbsd")
-		}
-
-		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		t.Run("ToFrom", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr())
-		})
-	})
-}
-
-func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr) {
-	ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
-	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
-
-	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
-		if nettest.ProtocolNotSupported(err) {
-			t.Skipf("not supported on %s", runtime.GOOS)
-		}
-		t.Fatal(err)
-	}
-
-	var wg sync.WaitGroup
-	reader := func() {
-		defer wg.Done()
-		b := make([]byte, 128)
-		n, cm, _, err := p.ReadFrom(b)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if !bytes.Equal(b[:n], data) {
-			t.Errorf("got %#v; want %#v", b[:n], data)
-			return
-		}
-		s := cm.String()
-		if strings.Contains(s, ",") {
-			t.Errorf("should be space-separated values: %s", s)
-			return
-		}
-	}
-	writer := func(toggle bool) {
-		defer wg.Done()
-		cm := ipv6.ControlMessage{
-			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
-			HopLimit:     1,
-			Src:          net.IPv6loopback,
-		}
-		if ifi != nil {
-			cm.IfIndex = ifi.Index
-		}
-		if err := p.SetControlMessage(cf, toggle); err != nil {
-			t.Error(err)
-			return
-		}
-		n, err := p.WriteTo(data, &cm, dst)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if n != len(data) {
-			t.Errorf("got %d; want %d", n, len(data))
-			return
-		}
-	}
-
-	const N = 10
-	wg.Add(N)
-	for i := 0; i < N; i++ {
-		go reader()
-	}
-	wg.Add(2 * N)
-	for i := 0; i < 2*N; i++ {
-		go writer(i%2 != 0)
-
-	}
-	wg.Add(N)
-	for i := 0; i < N; i++ {
-		go reader()
-	}
-	wg.Wait()
-}
diff --git a/ipv6/readwrite_go1_9_test.go b/ipv6/readwrite_go1_9_test.go
deleted file mode 100644
index 7110619..0000000
--- a/ipv6/readwrite_go1_9_test.go
+++ /dev/null
@@ -1,373 +0,0 @@
-// 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 ipv6_test
-
-import (
-	"bytes"
-	"fmt"
-	"net"
-	"runtime"
-	"strings"
-	"sync"
-	"testing"
-
-	"golang.org/x/net/internal/iana"
-	"golang.org/x/net/internal/nettest"
-	"golang.org/x/net/ipv6"
-)
-
-func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
-	switch runtime.GOOS {
-	case "js", "nacl", "plan9", "windows":
-		b.Skipf("not supported on %s", runtime.GOOS)
-	}
-
-	payload := []byte("HELLO-R-U-THERE")
-	iph := []byte{
-		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	}
-	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-	bb := make([]byte, 128)
-	cm := ipv6.ControlMessage{
-		TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
-		HopLimit:     1,
-		Src:          net.IPv6loopback,
-	}
-	if ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback); ifi != nil {
-		cm.IfIndex = ifi.Index
-	}
-
-	b.Run("UDP", func(b *testing.B) {
-		c, err := nettest.NewLocalPacketListener("udp6")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv6.FlagHopLimit | ipv6.FlagInterface
-		if err := p.SetControlMessage(cf, true); err != nil {
-			b.Fatal(err)
-		}
-		wms := []ipv6.Message{
-			{
-				Buffers: [][]byte{payload},
-				Addr:    dst,
-				OOB:     cm.Marshal(),
-			},
-		}
-		rms := []ipv6.Message{
-			{
-				Buffers: [][]byte{bb},
-				OOB:     ipv6.NewControlMessage(cf),
-			},
-		}
-		b.Run("Net", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := c.WriteTo(payload, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, err := c.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-		b.Run("ToFrom", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := p.WriteTo(payload, &cm, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, _, err := p.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-		b.Run("Batch", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := p.WriteBatch(wms, 0); err != nil {
-					b.Fatal(err)
-				}
-				if _, err := p.ReadBatch(rms, 0); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-	})
-	b.Run("IP", func(b *testing.B) {
-		switch runtime.GOOS {
-		case "netbsd":
-			b.Skip("need to configure gre on netbsd")
-		case "openbsd":
-			b.Skip("net.inet.gre.allow=0 by default on openbsd")
-		}
-
-		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
-		if err := p.SetControlMessage(cf, true); err != nil {
-			b.Fatal(err)
-		}
-		wms := []ipv6.Message{
-			{
-				Buffers: [][]byte{datagram},
-				Addr:    dst,
-				OOB:     cm.Marshal(),
-			},
-		}
-		rms := []ipv6.Message{
-			{
-				Buffers: [][]byte{bb},
-				OOB:     ipv6.NewControlMessage(cf),
-			},
-		}
-		b.Run("Net", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := c.WriteTo(datagram, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, err := c.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-		b.Run("ToFrom", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := p.WriteTo(datagram, &cm, dst); err != nil {
-					b.Fatal(err)
-				}
-				if _, _, _, err := p.ReadFrom(bb); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-		b.Run("Batch", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				if _, err := p.WriteBatch(wms, 0); err != nil {
-					b.Fatal(err)
-				}
-				if _, err := p.ReadBatch(rms, 0); err != nil {
-					b.Fatal(err)
-				}
-			}
-		})
-	})
-}
-
-func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
-	switch runtime.GOOS {
-	case "js", "nacl", "plan9", "windows":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
-	payload := []byte("HELLO-R-U-THERE")
-	iph := []byte{
-		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	}
-	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-
-	t.Run("UDP", func(t *testing.T) {
-		c, err := nettest.NewLocalPacketListener("udp6")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		t.Run("ToFrom", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
-		})
-		t.Run("Batch", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
-		})
-	})
-	t.Run("IP", func(t *testing.T) {
-		switch runtime.GOOS {
-		case "netbsd":
-			t.Skip("need to configure gre on netbsd")
-		case "openbsd":
-			t.Skip("net.inet.gre.allow=0 by default on openbsd")
-		}
-
-		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv6.NewPacketConn(c)
-		t.Run("ToFrom", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
-		})
-		t.Run("Batch", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
-		})
-	})
-}
-
-func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr, batch bool) {
-	ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
-	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
-
-	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
-		if nettest.ProtocolNotSupported(err) {
-			t.Skipf("not supported on %s", runtime.GOOS)
-		}
-		t.Fatal(err)
-	}
-
-	var wg sync.WaitGroup
-	reader := func() {
-		defer wg.Done()
-		b := make([]byte, 128)
-		n, cm, _, err := p.ReadFrom(b)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if !bytes.Equal(b[:n], data) {
-			t.Errorf("got %#v; want %#v", b[:n], data)
-			return
-		}
-		s := cm.String()
-		if strings.Contains(s, ",") {
-			t.Errorf("should be space-separated values: %s", s)
-			return
-		}
-	}
-	batchReader := func() {
-		defer wg.Done()
-		ms := []ipv6.Message{
-			{
-				Buffers: [][]byte{make([]byte, 128)},
-				OOB:     ipv6.NewControlMessage(cf),
-			},
-		}
-		n, err := p.ReadBatch(ms, 0)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if n != len(ms) {
-			t.Errorf("got %d; want %d", n, len(ms))
-			return
-		}
-		var cm ipv6.ControlMessage
-		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
-			t.Error(err)
-			return
-		}
-		b := ms[0].Buffers[0][:ms[0].N]
-		if !bytes.Equal(b, data) {
-			t.Errorf("got %#v; want %#v", b, data)
-			return
-		}
-		s := cm.String()
-		if strings.Contains(s, ",") {
-			t.Errorf("should be space-separated values: %s", s)
-			return
-		}
-	}
-	writer := func(toggle bool) {
-		defer wg.Done()
-		cm := ipv6.ControlMessage{
-			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
-			HopLimit:     1,
-			Src:          net.IPv6loopback,
-		}
-		if ifi != nil {
-			cm.IfIndex = ifi.Index
-		}
-		if err := p.SetControlMessage(cf, toggle); err != nil {
-			t.Error(err)
-			return
-		}
-		n, err := p.WriteTo(data, &cm, dst)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if n != len(data) {
-			t.Errorf("got %d; want %d", n, len(data))
-			return
-		}
-	}
-	batchWriter := func(toggle bool) {
-		defer wg.Done()
-		cm := ipv6.ControlMessage{
-			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
-			HopLimit:     1,
-			Src:          net.IPv6loopback,
-		}
-		if ifi != nil {
-			cm.IfIndex = ifi.Index
-		}
-		if err := p.SetControlMessage(cf, toggle); err != nil {
-			t.Error(err)
-			return
-		}
-		ms := []ipv6.Message{
-			{
-				Buffers: [][]byte{data},
-				OOB:     cm.Marshal(),
-				Addr:    dst,
-			},
-		}
-		n, err := p.WriteBatch(ms, 0)
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		if n != len(ms) {
-			t.Errorf("got %d; want %d", n, len(ms))
-			return
-		}
-		if ms[0].N != len(data) {
-			t.Errorf("got %d; want %d", ms[0].N, len(data))
-			return
-		}
-	}
-
-	const N = 10
-	wg.Add(N)
-	for i := 0; i < N; i++ {
-		if batch {
-			go batchReader()
-		} else {
-			go reader()
-		}
-	}
-	wg.Add(2 * N)
-	for i := 0; i < 2*N; i++ {
-		if batch {
-			go batchWriter(i%2 != 0)
-		} else {
-			go writer(i%2 != 0)
-		}
-	}
-	wg.Add(N)
-	for i := 0; i < N; i++ {
-		if batch {
-			go batchReader()
-		} else {
-			go reader()
-		}
-	}
-	wg.Wait()
-}
diff --git a/ipv6/readwrite_test.go b/ipv6/readwrite_test.go
index 668208f..e069cf8 100644
--- a/ipv6/readwrite_test.go
+++ b/ipv6/readwrite_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"net"
 	"runtime"
 	"strings"
@@ -63,6 +64,153 @@
 	})
 }
 
+func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
+	switch runtime.GOOS {
+	case "js", "nacl", "plan9", "windows":
+		b.Skipf("not supported on %s", runtime.GOOS)
+	}
+
+	payload := []byte("HELLO-R-U-THERE")
+	iph := []byte{
+		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	}
+	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
+	datagram := append(greh, append(iph, payload...)...)
+	bb := make([]byte, 128)
+	cm := ipv6.ControlMessage{
+		TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+		HopLimit:     1,
+		Src:          net.IPv6loopback,
+	}
+	if ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback); ifi != nil {
+		cm.IfIndex = ifi.Index
+	}
+
+	b.Run("UDP", func(b *testing.B) {
+		c, err := nettest.NewLocalPacketListener("udp6")
+		if err != nil {
+			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv6.NewPacketConn(c)
+		dst := c.LocalAddr()
+		cf := ipv6.FlagHopLimit | ipv6.FlagInterface
+		if err := p.SetControlMessage(cf, true); err != nil {
+			b.Fatal(err)
+		}
+		wms := []ipv6.Message{
+			{
+				Buffers: [][]byte{payload},
+				Addr:    dst,
+				OOB:     cm.Marshal(),
+			},
+		}
+		rms := []ipv6.Message{
+			{
+				Buffers: [][]byte{bb},
+				OOB:     ipv6.NewControlMessage(cf),
+			},
+		}
+		b.Run("Net", func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				if _, err := c.WriteTo(payload, dst); err != nil {
+					b.Fatal(err)
+				}
+				if _, _, err := c.ReadFrom(bb); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+		b.Run("ToFrom", func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				if _, err := p.WriteTo(payload, &cm, dst); err != nil {
+					b.Fatal(err)
+				}
+				if _, _, _, err := p.ReadFrom(bb); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+		b.Run("Batch", func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				if _, err := p.WriteBatch(wms, 0); err != nil {
+					b.Fatal(err)
+				}
+				if _, err := p.ReadBatch(rms, 0); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+	b.Run("IP", func(b *testing.B) {
+		switch runtime.GOOS {
+		case "netbsd":
+			b.Skip("need to configure gre on netbsd")
+		case "openbsd":
+			b.Skip("net.inet.gre.allow=0 by default on openbsd")
+		}
+
+		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
+		if err != nil {
+			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv6.NewPacketConn(c)
+		dst := c.LocalAddr()
+		cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+		if err := p.SetControlMessage(cf, true); err != nil {
+			b.Fatal(err)
+		}
+		wms := []ipv6.Message{
+			{
+				Buffers: [][]byte{datagram},
+				Addr:    dst,
+				OOB:     cm.Marshal(),
+			},
+		}
+		rms := []ipv6.Message{
+			{
+				Buffers: [][]byte{bb},
+				OOB:     ipv6.NewControlMessage(cf),
+			},
+		}
+		b.Run("Net", func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				if _, err := c.WriteTo(datagram, dst); err != nil {
+					b.Fatal(err)
+				}
+				if _, _, err := c.ReadFrom(bb); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+		b.Run("ToFrom", func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				if _, err := p.WriteTo(datagram, &cm, dst); err != nil {
+					b.Fatal(err)
+				}
+				if _, _, _, err := p.ReadFrom(bb); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+		b.Run("Batch", func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				if _, err := p.WriteBatch(wms, 0); err != nil {
+					b.Fatal(err)
+				}
+				if _, err := p.ReadBatch(rms, 0); err != nil {
+					b.Fatal(err)
+				}
+			}
+		})
+	})
+}
+
 func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
 	switch runtime.GOOS {
 	case "js", "nacl", "plan9", "windows":
@@ -146,3 +294,208 @@
 	}
 	wg.Wait()
 }
+
+func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
+	switch runtime.GOOS {
+	case "js", "nacl", "plan9", "windows":
+		t.Skipf("not supported on %s", runtime.GOOS)
+	}
+
+	payload := []byte("HELLO-R-U-THERE")
+	iph := []byte{
+		0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+		0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	}
+	greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
+	datagram := append(greh, append(iph, payload...)...)
+
+	t.Run("UDP", func(t *testing.T) {
+		c, err := nettest.NewLocalPacketListener("udp6")
+		if err != nil {
+			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv6.NewPacketConn(c)
+		t.Run("ToFrom", func(t *testing.T) {
+			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
+		})
+		t.Run("Batch", func(t *testing.T) {
+			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
+		})
+	})
+	t.Run("IP", func(t *testing.T) {
+		switch runtime.GOOS {
+		case "netbsd":
+			t.Skip("need to configure gre on netbsd")
+		case "openbsd":
+			t.Skip("net.inet.gre.allow=0 by default on openbsd")
+		}
+
+		c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
+		if err != nil {
+			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv6.NewPacketConn(c)
+		t.Run("ToFrom", func(t *testing.T) {
+			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
+		})
+		t.Run("Batch", func(t *testing.T) {
+			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
+		})
+	})
+}
+
+func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr, batch bool) {
+	ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
+	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
+
+	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
+		if nettest.ProtocolNotSupported(err) {
+			t.Skipf("not supported on %s", runtime.GOOS)
+		}
+		t.Fatal(err)
+	}
+
+	var wg sync.WaitGroup
+	reader := func() {
+		defer wg.Done()
+		b := make([]byte, 128)
+		n, cm, _, err := p.ReadFrom(b)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if !bytes.Equal(b[:n], data) {
+			t.Errorf("got %#v; want %#v", b[:n], data)
+			return
+		}
+		s := cm.String()
+		if strings.Contains(s, ",") {
+			t.Errorf("should be space-separated values: %s", s)
+			return
+		}
+	}
+	batchReader := func() {
+		defer wg.Done()
+		ms := []ipv6.Message{
+			{
+				Buffers: [][]byte{make([]byte, 128)},
+				OOB:     ipv6.NewControlMessage(cf),
+			},
+		}
+		n, err := p.ReadBatch(ms, 0)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if n != len(ms) {
+			t.Errorf("got %d; want %d", n, len(ms))
+			return
+		}
+		var cm ipv6.ControlMessage
+		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
+			t.Error(err)
+			return
+		}
+		b := ms[0].Buffers[0][:ms[0].N]
+		if !bytes.Equal(b, data) {
+			t.Errorf("got %#v; want %#v", b, data)
+			return
+		}
+		s := cm.String()
+		if strings.Contains(s, ",") {
+			t.Errorf("should be space-separated values: %s", s)
+			return
+		}
+	}
+	writer := func(toggle bool) {
+		defer wg.Done()
+		cm := ipv6.ControlMessage{
+			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+			HopLimit:     1,
+			Src:          net.IPv6loopback,
+		}
+		if ifi != nil {
+			cm.IfIndex = ifi.Index
+		}
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Error(err)
+			return
+		}
+		n, err := p.WriteTo(data, &cm, dst)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if n != len(data) {
+			t.Errorf("got %d; want %d", n, len(data))
+			return
+		}
+	}
+	batchWriter := func(toggle bool) {
+		defer wg.Done()
+		cm := ipv6.ControlMessage{
+			TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
+			HopLimit:     1,
+			Src:          net.IPv6loopback,
+		}
+		if ifi != nil {
+			cm.IfIndex = ifi.Index
+		}
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Error(err)
+			return
+		}
+		ms := []ipv6.Message{
+			{
+				Buffers: [][]byte{data},
+				OOB:     cm.Marshal(),
+				Addr:    dst,
+			},
+		}
+		n, err := p.WriteBatch(ms, 0)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if n != len(ms) {
+			t.Errorf("got %d; want %d", n, len(ms))
+			return
+		}
+		if ms[0].N != len(data) {
+			t.Errorf("got %d; want %d", ms[0].N, len(data))
+			return
+		}
+	}
+
+	const N = 10
+	wg.Add(N)
+	for i := 0; i < N; i++ {
+		if batch {
+			go batchReader()
+		} else {
+			go reader()
+		}
+	}
+	wg.Add(2 * N)
+	for i := 0; i < 2*N; i++ {
+		if batch {
+			go batchWriter(i%2 != 0)
+		} else {
+			go writer(i%2 != 0)
+		}
+	}
+	wg.Add(N)
+	for i := 0; i < N; i++ {
+		if batch {
+			go batchReader()
+		} else {
+			go reader()
+		}
+	}
+	wg.Wait()
+}