ipv4: drop support for go1.8 or below

Change-Id: I27e30dbb0cbbd5c3dd53333882a794f0ef1092ff
Reviewed-on: https://go-review.googlesource.com/c/net/+/162598
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/ipv4/batch.go b/ipv4/batch.go
index 5ce9b35..fbe0cfd 100644
--- a/ipv4/batch.go
+++ b/ipv4/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 ipv4
 
 import (
diff --git a/ipv4/packet.go b/ipv4/packet.go
index 966bb77..30e0951 100644
--- a/ipv4/packet.go
+++ b/ipv4/packet.go
@@ -29,7 +29,32 @@
 	if !c.ok() {
 		return nil, nil, nil, errInvalidConn
 	}
-	return c.readFrom(b)
+	c.rawOpt.RLock()
+	m := socket.Message{
+		Buffers: [][]byte{b},
+		OOB:     NewControlMessage(c.rawOpt.cflags),
+	}
+	c.rawOpt.RUnlock()
+	if err := c.RecvMsg(&m, 0); err != nil {
+		return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
+	}
+	var hs []byte
+	if hs, p, err = slicePacket(b[:m.N]); err != nil {
+		return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
+	}
+	if h, err = ParseHeader(hs); err != nil {
+		return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
+	}
+	if m.NN > 0 {
+		cm = new(ControlMessage)
+		if err := cm.Parse(m.OOB[:m.NN]); err != nil {
+			return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
+		}
+	}
+	if src, ok := m.Addr.(*net.IPAddr); ok && cm != nil {
+		cm.Src = src.IP
+	}
+	return
 }
 
 func slicePacket(b []byte) (h, p []byte, err error) {
@@ -64,5 +89,26 @@
 	if !c.ok() {
 		return errInvalidConn
 	}
-	return c.writeTo(h, p, cm)
+	m := socket.Message{
+		OOB: cm.Marshal(),
+	}
+	wh, err := h.Marshal()
+	if err != nil {
+		return err
+	}
+	m.Buffers = [][]byte{wh, p}
+	dst := new(net.IPAddr)
+	if cm != nil {
+		if ip := cm.Dst.To4(); ip != nil {
+			dst.IP = ip
+		}
+	}
+	if dst.IP == nil {
+		dst.IP = h.Dst
+	}
+	m.Addr = dst
+	if err := c.SendMsg(&m, 0); err != nil {
+		return &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Addr: opAddr(dst), Err: err}
+	}
+	return nil
 }
diff --git a/ipv4/packet_go1_8.go b/ipv4/packet_go1_8.go
deleted file mode 100644
index b47d186..0000000
--- a/ipv4/packet_go1_8.go
+++ /dev/null
@@ -1,56 +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 !go1.9
-
-package ipv4
-
-import "net"
-
-func (c *packetHandler) readFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) {
-	c.rawOpt.RLock()
-	oob := NewControlMessage(c.rawOpt.cflags)
-	c.rawOpt.RUnlock()
-	n, nn, _, src, err := c.ReadMsgIP(b, oob)
-	if err != nil {
-		return nil, nil, nil, err
-	}
-	var hs []byte
-	if hs, p, err = slicePacket(b[:n]); err != nil {
-		return nil, nil, nil, err
-	}
-	if h, err = ParseHeader(hs); err != nil {
-		return nil, nil, nil, err
-	}
-	if nn > 0 {
-		cm = new(ControlMessage)
-		if err := cm.Parse(oob[:nn]); err != nil {
-			return nil, nil, nil, err
-		}
-	}
-	if src != nil && cm != nil {
-		cm.Src = src.IP
-	}
-	return
-}
-
-func (c *packetHandler) writeTo(h *Header, p []byte, cm *ControlMessage) error {
-	oob := cm.Marshal()
-	wh, err := h.Marshal()
-	if err != nil {
-		return err
-	}
-	dst := new(net.IPAddr)
-	if cm != nil {
-		if ip := cm.Dst.To4(); ip != nil {
-			dst.IP = ip
-		}
-	}
-	if dst.IP == nil {
-		dst.IP = h.Dst
-	}
-	wh = append(wh, p...)
-	_, _, err = c.WriteMsgIP(wh, oob, dst)
-	return err
-}
diff --git a/ipv4/packet_go1_9.go b/ipv4/packet_go1_9.go
deleted file mode 100644
index 082c36d..0000000
--- a/ipv4/packet_go1_9.go
+++ /dev/null
@@ -1,67 +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 ipv4
-
-import (
-	"net"
-
-	"golang.org/x/net/internal/socket"
-)
-
-func (c *packetHandler) readFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) {
-	c.rawOpt.RLock()
-	m := socket.Message{
-		Buffers: [][]byte{b},
-		OOB:     NewControlMessage(c.rawOpt.cflags),
-	}
-	c.rawOpt.RUnlock()
-	if err := c.RecvMsg(&m, 0); err != nil {
-		return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
-	}
-	var hs []byte
-	if hs, p, err = slicePacket(b[:m.N]); err != nil {
-		return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
-	}
-	if h, err = ParseHeader(hs); err != nil {
-		return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
-	}
-	if m.NN > 0 {
-		cm = new(ControlMessage)
-		if err := cm.Parse(m.OOB[:m.NN]); err != nil {
-			return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err}
-		}
-	}
-	if src, ok := m.Addr.(*net.IPAddr); ok && cm != nil {
-		cm.Src = src.IP
-	}
-	return
-}
-
-func (c *packetHandler) writeTo(h *Header, p []byte, cm *ControlMessage) error {
-	m := socket.Message{
-		OOB: cm.Marshal(),
-	}
-	wh, err := h.Marshal()
-	if err != nil {
-		return err
-	}
-	m.Buffers = [][]byte{wh, p}
-	dst := new(net.IPAddr)
-	if cm != nil {
-		if ip := cm.Dst.To4(); ip != nil {
-			dst.IP = ip
-		}
-	}
-	if dst.IP == nil {
-		dst.IP = h.Dst
-	}
-	m.Addr = dst
-	if err := c.SendMsg(&m, 0); err != nil {
-		return &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Addr: opAddr(dst), Err: err}
-	}
-	return nil
-}
diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go
index a7c892d..e8b21c5 100644
--- a/ipv4/payload_cmsg.go
+++ b/ipv4/payload_cmsg.go
@@ -6,7 +6,11 @@
 
 package ipv4
 
-import "net"
+import (
+	"net"
+
+	"golang.org/x/net/internal/socket"
+)
 
 // ReadFrom reads a payload of the received IPv4 datagram, from the
 // endpoint c, copying the payload into b. It returns the number of
@@ -16,7 +20,42 @@
 	if !c.ok() {
 		return 0, nil, nil, errInvalidConn
 	}
-	return c.readFrom(b)
+	c.rawOpt.RLock()
+	m := socket.Message{
+		OOB: NewControlMessage(c.rawOpt.cflags),
+	}
+	c.rawOpt.RUnlock()
+	switch c.PacketConn.(type) {
+	case *net.UDPConn:
+		m.Buffers = [][]byte{b}
+		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:
+		h := make([]byte, HeaderLen)
+		m.Buffers = [][]byte{h, b}
+		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}
+		}
+		hdrlen := int(h[0]&0x0f) << 2
+		if hdrlen > len(h) {
+			d := hdrlen - len(h)
+			copy(b, b[d:])
+			m.N -= d
+		} else {
+			m.N -= hdrlen
+		}
+	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 = netAddrToIP4(m.Addr)
+	}
+	return m.N, cm, m.Addr, nil
 }
 
 // WriteTo writes a payload of the IPv4 datagram, to the destination
@@ -29,5 +68,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/ipv4/payload_cmsg_go1_8.go b/ipv4/payload_cmsg_go1_8.go
deleted file mode 100644
index 15a27b7..0000000
--- a/ipv4/payload_cmsg_go1_8.go
+++ /dev/null
@@ -1,59 +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 !go1.9
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-
-package ipv4
-
-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:
-		nb := make([]byte, maxHeaderLen+len(b))
-		if n, nn, _, 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, &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 = netAddrToIP4(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/ipv4/payload_cmsg_go1_9.go b/ipv4/payload_cmsg_go1_9.go
deleted file mode 100644
index aab3b22..0000000
--- a/ipv4/payload_cmsg_go1_9.go
+++ /dev/null
@@ -1,67 +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 ipv4
-
-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{
-		OOB: NewControlMessage(c.rawOpt.cflags),
-	}
-	c.rawOpt.RUnlock()
-	switch c.PacketConn.(type) {
-	case *net.UDPConn:
-		m.Buffers = [][]byte{b}
-		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:
-		h := make([]byte, HeaderLen)
-		m.Buffers = [][]byte{h, b}
-		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}
-		}
-		hdrlen := int(h[0]&0x0f) << 2
-		if hdrlen > len(h) {
-			d := hdrlen - len(h)
-			copy(b, b[d:])
-			m.N -= d
-		} else {
-			m.N -= hdrlen
-		}
-	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 = netAddrToIP4(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/ipv4/readwrite_go1_8_test.go b/ipv4/readwrite_go1_8_test.go
deleted file mode 100644
index 83bf927..0000000
--- a/ipv4/readwrite_go1_8_test.go
+++ /dev/null
@@ -1,248 +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 !go1.9
-
-package ipv4_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/ipv4"
-)
-
-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, err := (&ipv4.Header{
-		Version:  ipv4.Version,
-		Len:      ipv4.HeaderLen,
-		TotalLen: ipv4.HeaderLen + len(payload),
-		TTL:      1,
-		Protocol: iana.ProtocolReserved,
-		Src:      net.IPv4(192, 0, 2, 1),
-		Dst:      net.IPv4(192, 0, 2, 254),
-	}).Marshal()
-	if err != nil {
-		b.Fatal(err)
-	}
-	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-	bb := make([]byte, 128)
-	cm := ipv4.ControlMessage{
-		Src: net.IPv4(127, 0, 0, 1),
-	}
-	if ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); ifi != nil {
-		cm.IfIndex = ifi.Index
-	}
-
-	b.Run("UDP", func(b *testing.B) {
-		c, err := nettest.NewLocalPacketListener("udp4")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv4.FlagTTL | ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv4.FlagTTL | ipv4.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(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, err := (&ipv4.Header{
-		Version:  ipv4.Version,
-		Len:      ipv4.HeaderLen,
-		TotalLen: ipv4.HeaderLen + len(payload),
-		TTL:      1,
-		Protocol: iana.ProtocolReserved,
-		Src:      net.IPv4(192, 0, 2, 1),
-		Dst:      net.IPv4(192, 0, 2, 254),
-	}).Marshal()
-	if err != nil {
-		t.Fatal(err)
-	}
-	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-
-	t.Run("UDP", func(t *testing.T) {
-		c, err := nettest.NewLocalPacketListener("udp4")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.NewPacketConn(c)
-		t.Run("ToFrom", func(t *testing.T) {
-			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr())
-		})
-	})
-}
-
-func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv4.PacketConn, data []byte, dst net.Addr) {
-	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
-	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
-
-	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 := ipv4.ControlMessage{
-			Src: net.IPv4(127, 0, 0, 1),
-		}
-		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/ipv4/readwrite_go1_9_test.go b/ipv4/readwrite_go1_9_test.go
deleted file mode 100644
index ef76c8a..0000000
--- a/ipv4/readwrite_go1_9_test.go
+++ /dev/null
@@ -1,388 +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 ipv4_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/ipv4"
-)
-
-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, err := (&ipv4.Header{
-		Version:  ipv4.Version,
-		Len:      ipv4.HeaderLen,
-		TotalLen: ipv4.HeaderLen + len(payload),
-		TTL:      1,
-		Protocol: iana.ProtocolReserved,
-		Src:      net.IPv4(192, 0, 2, 1),
-		Dst:      net.IPv4(192, 0, 2, 254),
-	}).Marshal()
-	if err != nil {
-		b.Fatal(err)
-	}
-	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-	bb := make([]byte, 128)
-	cm := ipv4.ControlMessage{
-		Src: net.IPv4(127, 0, 0, 1),
-	}
-	if ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); ifi != nil {
-		cm.IfIndex = ifi.Index
-	}
-
-	b.Run("UDP", func(b *testing.B) {
-		c, err := nettest.NewLocalPacketListener("udp4")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv4.FlagTTL | ipv4.FlagInterface
-		if err := p.SetControlMessage(cf, true); err != nil {
-			b.Fatal(err)
-		}
-		wms := []ipv4.Message{
-			{
-				Buffers: [][]byte{payload},
-				Addr:    dst,
-				OOB:     cm.Marshal(),
-			},
-		}
-		rms := []ipv4.Message{
-			{
-				Buffers: [][]byte{bb},
-				OOB:     ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
-		if err != nil {
-			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.NewPacketConn(c)
-		dst := c.LocalAddr()
-		cf := ipv4.FlagTTL | ipv4.FlagInterface
-		if err := p.SetControlMessage(cf, true); err != nil {
-			b.Fatal(err)
-		}
-		wms := []ipv4.Message{
-			{
-				Buffers: [][]byte{datagram},
-				Addr:    dst,
-				OOB:     cm.Marshal(),
-			},
-		}
-		rms := []ipv4.Message{
-			{
-				Buffers: [][]byte{bb},
-				OOB:     ipv4.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, err := (&ipv4.Header{
-		Version:  ipv4.Version,
-		Len:      ipv4.HeaderLen,
-		TotalLen: ipv4.HeaderLen + len(payload),
-		TTL:      1,
-		Protocol: iana.ProtocolReserved,
-		Src:      net.IPv4(192, 0, 2, 1),
-		Dst:      net.IPv4(192, 0, 2, 254),
-	}).Marshal()
-	if err != nil {
-		t.Fatal(err)
-	}
-	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
-	datagram := append(greh, append(iph, payload...)...)
-
-	t.Run("UDP", func(t *testing.T) {
-		c, err := nettest.NewLocalPacketListener("udp4")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
-		if err != nil {
-			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
-		}
-		defer c.Close()
-		p := ipv4.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 *ipv4.PacketConn, data []byte, dst net.Addr, batch bool) {
-	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
-	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
-
-	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 := []ipv4.Message{
-			{
-				Buffers: [][]byte{make([]byte, 128)},
-				OOB:     ipv4.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 ipv4.ControlMessage
-		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
-			t.Error(err)
-			return
-		}
-		var b []byte
-		if _, ok := dst.(*net.IPAddr); ok {
-			var h ipv4.Header
-			if err := h.Parse(ms[0].Buffers[0][:ms[0].N]); err != nil {
-				t.Error(err)
-				return
-			}
-			b = ms[0].Buffers[0][h.Len:ms[0].N]
-		} else {
-			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 := ipv4.ControlMessage{
-			Src: net.IPv4(127, 0, 0, 1),
-		}
-		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 := ipv4.ControlMessage{
-			Src: net.IPv4(127, 0, 0, 1),
-		}
-		if ifi != nil {
-			cm.IfIndex = ifi.Index
-		}
-		if err := p.SetControlMessage(cf, toggle); err != nil {
-			t.Error(err)
-			return
-		}
-		ms := []ipv4.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/ipv4/readwrite_test.go b/ipv4/readwrite_test.go
index 1b7f748..d6e35be 100644
--- a/ipv4/readwrite_test.go
+++ b/ipv4/readwrite_test.go
@@ -6,12 +6,14 @@
 
 import (
 	"bytes"
+	"fmt"
 	"net"
 	"runtime"
 	"strings"
 	"sync"
 	"testing"
 
+	"golang.org/x/net/internal/iana"
 	"golang.org/x/net/internal/nettest"
 	"golang.org/x/net/ipv4"
 )
@@ -59,6 +61,156 @@
 	})
 }
 
+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, err := (&ipv4.Header{
+		Version:  ipv4.Version,
+		Len:      ipv4.HeaderLen,
+		TotalLen: ipv4.HeaderLen + len(payload),
+		TTL:      1,
+		Protocol: iana.ProtocolReserved,
+		Src:      net.IPv4(192, 0, 2, 1),
+		Dst:      net.IPv4(192, 0, 2, 254),
+	}).Marshal()
+	if err != nil {
+		b.Fatal(err)
+	}
+	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
+	datagram := append(greh, append(iph, payload...)...)
+	bb := make([]byte, 128)
+	cm := ipv4.ControlMessage{
+		Src: net.IPv4(127, 0, 0, 1),
+	}
+	if ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); ifi != nil {
+		cm.IfIndex = ifi.Index
+	}
+
+	b.Run("UDP", func(b *testing.B) {
+		c, err := nettest.NewLocalPacketListener("udp4")
+		if err != nil {
+			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv4.NewPacketConn(c)
+		dst := c.LocalAddr()
+		cf := ipv4.FlagTTL | ipv4.FlagInterface
+		if err := p.SetControlMessage(cf, true); err != nil {
+			b.Fatal(err)
+		}
+		wms := []ipv4.Message{
+			{
+				Buffers: [][]byte{payload},
+				Addr:    dst,
+				OOB:     cm.Marshal(),
+			},
+		}
+		rms := []ipv4.Message{
+			{
+				Buffers: [][]byte{bb},
+				OOB:     ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
+		if err != nil {
+			b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv4.NewPacketConn(c)
+		dst := c.LocalAddr()
+		cf := ipv4.FlagTTL | ipv4.FlagInterface
+		if err := p.SetControlMessage(cf, true); err != nil {
+			b.Fatal(err)
+		}
+		wms := []ipv4.Message{
+			{
+				Buffers: [][]byte{datagram},
+				Addr:    dst,
+				OOB:     cm.Marshal(),
+			},
+		}
+		rms := []ipv4.Message{
+			{
+				Buffers: [][]byte{bb},
+				OOB:     ipv4.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":
@@ -138,3 +290,220 @@
 	}
 	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, err := (&ipv4.Header{
+		Version:  ipv4.Version,
+		Len:      ipv4.HeaderLen,
+		TotalLen: ipv4.HeaderLen + len(payload),
+		TTL:      1,
+		Protocol: iana.ProtocolReserved,
+		Src:      net.IPv4(192, 0, 2, 1),
+		Dst:      net.IPv4(192, 0, 2, 254),
+	}).Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
+	datagram := append(greh, append(iph, payload...)...)
+
+	t.Run("UDP", func(t *testing.T) {
+		c, err := nettest.NewLocalPacketListener("udp4")
+		if err != nil {
+			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
+		if err != nil {
+			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
+		}
+		defer c.Close()
+		p := ipv4.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 *ipv4.PacketConn, data []byte, dst net.Addr, batch bool) {
+	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
+	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
+
+	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 := []ipv4.Message{
+			{
+				Buffers: [][]byte{make([]byte, 128)},
+				OOB:     ipv4.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 ipv4.ControlMessage
+		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
+			t.Error(err)
+			return
+		}
+		var b []byte
+		if _, ok := dst.(*net.IPAddr); ok {
+			var h ipv4.Header
+			if err := h.Parse(ms[0].Buffers[0][:ms[0].N]); err != nil {
+				t.Error(err)
+				return
+			}
+			b = ms[0].Buffers[0][h.Len:ms[0].N]
+		} else {
+			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 := ipv4.ControlMessage{
+			Src: net.IPv4(127, 0, 0, 1),
+		}
+		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 := ipv4.ControlMessage{
+			Src: net.IPv4(127, 0, 0, 1),
+		}
+		if ifi != nil {
+			cm.IfIndex = ifi.Index
+		}
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Error(err)
+			return
+		}
+		ms := []ipv4.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()
+}