icmp: use subtests

This change reorganizes the existing test cases for supporting new
messages and extension in upcoming CLs. It also renames ping_test.go
to diag_test.go for clarification.

Updates golang/go#24440.

Change-Id: I5753a2ddbe76423d996a37f583fcf32b65d380e9
Reviewed-on: https://go-review.googlesource.com/63995
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/icmp/diag_test.go b/icmp/diag_test.go
new file mode 100644
index 0000000..6a87a60
--- /dev/null
+++ b/icmp/diag_test.go
@@ -0,0 +1,224 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package icmp_test
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"os"
+	"runtime"
+	"sync"
+	"testing"
+	"time"
+
+	"golang.org/x/net/icmp"
+	"golang.org/x/net/internal/iana"
+	"golang.org/x/net/internal/nettest"
+	"golang.org/x/net/ipv4"
+	"golang.org/x/net/ipv6"
+)
+
+type diagTest struct {
+	network, address string
+	protocol         int
+	m                icmp.Message
+}
+
+func TestDiag(t *testing.T) {
+	if testing.Short() {
+		t.Skip("avoid external network")
+	}
+
+	t.Run("Ping/NonPrivileged", func(t *testing.T) {
+		switch runtime.GOOS {
+		case "darwin":
+		case "linux":
+			t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
+		default:
+			t.Logf("not supported on %s", runtime.GOOS)
+			return
+		}
+		for i, dt := range []diagTest{
+			{
+				"udp4", "0.0.0.0", iana.ProtocolICMP,
+				icmp.Message{
+					Type: ipv4.ICMPTypeEcho, Code: 0,
+					Body: &icmp.Echo{
+						ID:   os.Getpid() & 0xffff,
+						Data: []byte("HELLO-R-U-THERE"),
+					},
+				},
+			},
+
+			{
+				"udp6", "::", iana.ProtocolIPv6ICMP,
+				icmp.Message{
+					Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+					Body: &icmp.Echo{
+						ID:   os.Getpid() & 0xffff,
+						Data: []byte("HELLO-R-U-THERE"),
+					},
+				},
+			},
+		} {
+			if err := doDiag(dt, i); err != nil {
+				t.Error(err)
+			}
+		}
+	})
+	t.Run("Ping/Privileged", func(t *testing.T) {
+		if m, ok := nettest.SupportsRawIPSocket(); !ok {
+			t.Log(m)
+			return
+		}
+		for i, dt := range []diagTest{
+			{
+				"ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
+				icmp.Message{
+					Type: ipv4.ICMPTypeEcho, Code: 0,
+					Body: &icmp.Echo{
+						ID:   os.Getpid() & 0xffff,
+						Data: []byte("HELLO-R-U-THERE"),
+					},
+				},
+			},
+
+			{
+				"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
+				icmp.Message{
+					Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+					Body: &icmp.Echo{
+						ID:   os.Getpid() & 0xffff,
+						Data: []byte("HELLO-R-U-THERE"),
+					},
+				},
+			},
+		} {
+			if err := doDiag(dt, i); err != nil {
+				t.Error(err)
+			}
+		}
+	})
+}
+
+func doDiag(dt diagTest, seq int) error {
+	c, err := icmp.ListenPacket(dt.network, dt.address)
+	if err != nil {
+		return err
+	}
+	defer c.Close()
+
+	dst, err := googleAddr(c, dt.protocol)
+	if err != nil {
+		return err
+	}
+
+	if dt.network != "udp6" && dt.protocol == iana.ProtocolIPv6ICMP {
+		var f ipv6.ICMPFilter
+		f.SetAll(true)
+		f.Accept(ipv6.ICMPTypeDestinationUnreachable)
+		f.Accept(ipv6.ICMPTypePacketTooBig)
+		f.Accept(ipv6.ICMPTypeTimeExceeded)
+		f.Accept(ipv6.ICMPTypeParameterProblem)
+		f.Accept(ipv6.ICMPTypeEchoReply)
+		if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
+			return err
+		}
+	}
+
+	switch m := dt.m.Body.(type) {
+	case *icmp.Echo:
+		m.Seq = 1 << uint(seq)
+	}
+	wb, err := dt.m.Marshal(nil)
+	if err != nil {
+		return err
+	}
+	if n, err := c.WriteTo(wb, dst); err != nil {
+		return err
+	} else if n != len(wb) {
+		return fmt.Errorf("got %v; want %v", n, len(wb))
+	}
+
+	rb := make([]byte, 1500)
+	if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
+		return err
+	}
+	n, peer, err := c.ReadFrom(rb)
+	if err != nil {
+		return err
+	}
+	rm, err := icmp.ParseMessage(dt.protocol, rb[:n])
+	if err != nil {
+		return err
+	}
+	switch {
+	case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply:
+		fallthrough
+	case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply:
+		return nil
+	default:
+		return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
+	}
+}
+
+func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
+	host := "ipv4.google.com"
+	if protocol == iana.ProtocolIPv6ICMP {
+		host = "ipv6.google.com"
+	}
+	ips, err := net.LookupIP(host)
+	if err != nil {
+		return nil, err
+	}
+	netaddr := func(ip net.IP) (net.Addr, error) {
+		switch c.LocalAddr().(type) {
+		case *net.UDPAddr:
+			return &net.UDPAddr{IP: ip}, nil
+		case *net.IPAddr:
+			return &net.IPAddr{IP: ip}, nil
+		default:
+			return nil, errors.New("neither UDPAddr nor IPAddr")
+		}
+	}
+	if len(ips) > 0 {
+		return netaddr(ips[0])
+	}
+	return nil, errors.New("no A or AAAA record")
+}
+
+func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
+	if testing.Short() {
+		t.Skip("avoid external network")
+	}
+	switch runtime.GOOS {
+	case "darwin":
+	case "linux":
+		t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
+	default:
+		t.Skipf("not supported on %s", runtime.GOOS)
+	}
+
+	network, address := "udp4", "127.0.0.1"
+	if !nettest.SupportsIPv4() {
+		network, address = "udp6", "::1"
+	}
+	const N = 1000
+	var wg sync.WaitGroup
+	wg.Add(N)
+	for i := 0; i < N; i++ {
+		go func() {
+			defer wg.Done()
+			c, err := icmp.ListenPacket(network, address)
+			if err != nil {
+				t.Error(err)
+				return
+			}
+			c.Close()
+		}()
+	}
+	wg.Wait()
+}
diff --git a/icmp/extension_test.go b/icmp/extension_test.go
index 0b3f7b9..193859e 100644
--- a/icmp/extension_test.go
+++ b/icmp/extension_test.go
@@ -5,6 +5,7 @@
 package icmp
 
 import (
+	"fmt"
 	"net"
 	"reflect"
 	"testing"
@@ -12,185 +13,16 @@
 	"golang.org/x/net/internal/iana"
 )
 
-var marshalAndParseExtensionTests = []struct {
-	proto int
-	hdr   []byte
-	obj   []byte
-	exts  []Extension
-}{
-	// MPLS label stack with no label
-	{
-		proto: iana.ProtocolICMP,
-		hdr: []byte{
-			0x20, 0x00, 0x00, 0x00,
-		},
-		obj: []byte{
-			0x00, 0x04, 0x01, 0x01,
-		},
-		exts: []Extension{
-			&MPLSLabelStack{
-				Class: classMPLSLabelStack,
-				Type:  typeIncomingMPLSLabelStack,
-			},
-		},
-	},
-	// MPLS label stack with a single label
-	{
-		proto: iana.ProtocolIPv6ICMP,
-		hdr: []byte{
-			0x20, 0x00, 0x00, 0x00,
-		},
-		obj: []byte{
-			0x00, 0x08, 0x01, 0x01,
-			0x03, 0xe8, 0xe9, 0xff,
-		},
-		exts: []Extension{
-			&MPLSLabelStack{
-				Class: classMPLSLabelStack,
-				Type:  typeIncomingMPLSLabelStack,
-				Labels: []MPLSLabel{
-					{
-						Label: 16014,
-						TC:    0x4,
-						S:     true,
-						TTL:   255,
-					},
-				},
-			},
-		},
-	},
-	// MPLS label stack with multiple labels
-	{
-		proto: iana.ProtocolICMP,
-		hdr: []byte{
-			0x20, 0x00, 0x00, 0x00,
-		},
-		obj: []byte{
-			0x00, 0x0c, 0x01, 0x01,
-			0x03, 0xe8, 0xde, 0xfe,
-			0x03, 0xe8, 0xe1, 0xff,
-		},
-		exts: []Extension{
-			&MPLSLabelStack{
-				Class: classMPLSLabelStack,
-				Type:  typeIncomingMPLSLabelStack,
-				Labels: []MPLSLabel{
-					{
-						Label: 16013,
-						TC:    0x7,
-						S:     false,
-						TTL:   254,
-					},
-					{
-						Label: 16014,
-						TC:    0,
-						S:     true,
-						TTL:   255,
-					},
-				},
-			},
-		},
-	},
-	// Interface information with no attribute
-	{
-		proto: iana.ProtocolICMP,
-		hdr: []byte{
-			0x20, 0x00, 0x00, 0x00,
-		},
-		obj: []byte{
-			0x00, 0x04, 0x02, 0x00,
-		},
-		exts: []Extension{
-			&InterfaceInfo{
-				Class: classInterfaceInfo,
-			},
-		},
-	},
-	// Interface information with ifIndex and name
-	{
-		proto: iana.ProtocolICMP,
-		hdr: []byte{
-			0x20, 0x00, 0x00, 0x00,
-		},
-		obj: []byte{
-			0x00, 0x10, 0x02, 0x0a,
-			0x00, 0x00, 0x00, 0x10,
-			0x08, byte('e'), byte('n'), byte('1'),
-			byte('0'), byte('1'), 0x00, 0x00,
-		},
-		exts: []Extension{
-			&InterfaceInfo{
-				Class: classInterfaceInfo,
-				Type:  0x0a,
-				Interface: &net.Interface{
-					Index: 16,
-					Name:  "en101",
-				},
-			},
-		},
-	},
-	// Interface information with ifIndex, IPAddr, name and MTU
-	{
-		proto: iana.ProtocolIPv6ICMP,
-		hdr: []byte{
-			0x20, 0x00, 0x00, 0x00,
-		},
-		obj: []byte{
-			0x00, 0x28, 0x02, 0x0f,
-			0x00, 0x00, 0x00, 0x0f,
-			0x00, 0x02, 0x00, 0x00,
-			0xfe, 0x80, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x01,
-			0x08, byte('e'), byte('n'), byte('1'),
-			byte('0'), byte('1'), 0x00, 0x00,
-			0x00, 0x00, 0x20, 0x00,
-		},
-		exts: []Extension{
-			&InterfaceInfo{
-				Class: classInterfaceInfo,
-				Type:  0x0f,
-				Interface: &net.Interface{
-					Index: 15,
-					Name:  "en101",
-					MTU:   8192,
-				},
-				Addr: &net.IPAddr{
-					IP:   net.ParseIP("fe80::1"),
-					Zone: "en101",
-				},
-			},
-		},
-	},
-}
-
 func TestMarshalAndParseExtension(t *testing.T) {
-	for i, tt := range marshalAndParseExtensionTests {
-		for j, ext := range tt.exts {
-			var err error
-			var b []byte
-			switch ext := ext.(type) {
-			case *MPLSLabelStack:
-				b, err = ext.Marshal(tt.proto)
-				if err != nil {
-					t.Errorf("#%v/%v: %v", i, j, err)
-					continue
-				}
-			case *InterfaceInfo:
-				b, err = ext.Marshal(tt.proto)
-				if err != nil {
-					t.Errorf("#%v/%v: %v", i, j, err)
-					continue
-				}
-			}
-			if !reflect.DeepEqual(b, tt.obj) {
-				t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj)
-				continue
-			}
+	fn := func(t *testing.T, proto int, hdr, obj []byte, te Extension) error {
+		b, err := te.Marshal(proto)
+		if err != nil {
+			return err
 		}
-
-		for j, wire := range []struct {
+		if !reflect.DeepEqual(b, obj) {
+			return fmt.Errorf("got %#v; want %#v", b, obj)
+		}
+		for i, wire := range []struct {
 			data     []byte // original datagram
 			inlattr  int    // length of padded original datagram, a hint
 			outlattr int    // length of padded original datagram, a want
@@ -203,55 +35,203 @@
 			{make([]byte, 128), 128, -1, errNoExtension},
 			{make([]byte, 128), 129, -1, errNoExtension},
 
-			{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 127, 128, nil},
-			{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 128, 128, nil},
-			{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 129, 128, nil},
+			{append(make([]byte, 128), append(hdr, obj...)...), 127, 128, nil},
+			{append(make([]byte, 128), append(hdr, obj...)...), 128, 128, nil},
+			{append(make([]byte, 128), append(hdr, obj...)...), 129, 128, nil},
 
-			{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 511, -1, errNoExtension},
-			{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 512, 512, nil},
-			{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 513, -1, errNoExtension},
+			{append(make([]byte, 512), append(hdr, obj...)...), 511, -1, errNoExtension},
+			{append(make([]byte, 512), append(hdr, obj...)...), 512, 512, nil},
+			{append(make([]byte, 512), append(hdr, obj...)...), 513, -1, errNoExtension},
 		} {
 			exts, l, err := parseExtensions(wire.data, wire.inlattr)
 			if err != wire.err {
-				t.Errorf("#%v/%v: got %v; want %v", i, j, err, wire.err)
-				continue
+				return fmt.Errorf("#%d: got %v; want %v", i, err, wire.err)
 			}
 			if wire.err != nil {
 				continue
 			}
 			if l != wire.outlattr {
-				t.Errorf("#%v/%v: got %v; want %v", i, j, l, wire.outlattr)
+				return fmt.Errorf("#%d: got %d; want %d", i, l, wire.outlattr)
 			}
-			if !reflect.DeepEqual(exts, tt.exts) {
-				for j, ext := range exts {
-					switch ext := ext.(type) {
-					case *MPLSLabelStack:
-						want := tt.exts[j].(*MPLSLabelStack)
-						t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
-					case *InterfaceInfo:
-						want := tt.exts[j].(*InterfaceInfo)
-						t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
-					}
-				}
-				continue
+			if !reflect.DeepEqual(exts, []Extension{te}) {
+				return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], te)
 			}
 		}
+		return nil
 	}
-}
 
-var parseInterfaceNameTests = []struct {
-	b []byte
-	error
-}{
-	{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
-	{[]byte{4, 'e', 'n', '0'}, nil},
-	{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
-	{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
+	t.Run("MPLSLabelStack", func(t *testing.T) {
+		for _, et := range []struct {
+			proto int
+			hdr   []byte
+			obj   []byte
+			ext   Extension
+		}{
+			// MPLS label stack with no label
+			{
+				proto: iana.ProtocolICMP,
+				hdr: []byte{
+					0x20, 0x00, 0x00, 0x00,
+				},
+				obj: []byte{
+					0x00, 0x04, 0x01, 0x01,
+				},
+				ext: &MPLSLabelStack{
+					Class: classMPLSLabelStack,
+					Type:  typeIncomingMPLSLabelStack,
+				},
+			},
+			// MPLS label stack with a single label
+			{
+				proto: iana.ProtocolIPv6ICMP,
+				hdr: []byte{
+					0x20, 0x00, 0x00, 0x00,
+				},
+				obj: []byte{
+					0x00, 0x08, 0x01, 0x01,
+					0x03, 0xe8, 0xe9, 0xff,
+				},
+				ext: &MPLSLabelStack{
+					Class: classMPLSLabelStack,
+					Type:  typeIncomingMPLSLabelStack,
+					Labels: []MPLSLabel{
+						{
+							Label: 16014,
+							TC:    0x4,
+							S:     true,
+							TTL:   255,
+						},
+					},
+				},
+			},
+			// MPLS label stack with multiple labels
+			{
+				proto: iana.ProtocolICMP,
+				hdr: []byte{
+					0x20, 0x00, 0x00, 0x00,
+				},
+				obj: []byte{
+					0x00, 0x0c, 0x01, 0x01,
+					0x03, 0xe8, 0xde, 0xfe,
+					0x03, 0xe8, 0xe1, 0xff,
+				},
+				ext: &MPLSLabelStack{
+					Class: classMPLSLabelStack,
+					Type:  typeIncomingMPLSLabelStack,
+					Labels: []MPLSLabel{
+						{
+							Label: 16013,
+							TC:    0x7,
+							S:     false,
+							TTL:   254,
+						},
+						{
+							Label: 16014,
+							TC:    0,
+							S:     true,
+							TTL:   255,
+						},
+					},
+				},
+			},
+		} {
+			if err := fn(t, et.proto, et.hdr, et.obj, et.ext); err != nil {
+				t.Error(err)
+			}
+		}
+	})
+	t.Run("InterfaceInfo", func(t *testing.T) {
+		for _, et := range []struct {
+			proto int
+			hdr   []byte
+			obj   []byte
+			ext   Extension
+		}{
+			// Interface information with no attribute
+			{
+				proto: iana.ProtocolICMP,
+				hdr: []byte{
+					0x20, 0x00, 0x00, 0x00,
+				},
+				obj: []byte{
+					0x00, 0x04, 0x02, 0x00,
+				},
+				ext: &InterfaceInfo{
+					Class: classInterfaceInfo,
+				},
+			},
+			// Interface information with ifIndex and name
+			{
+				proto: iana.ProtocolICMP,
+				hdr: []byte{
+					0x20, 0x00, 0x00, 0x00,
+				},
+				obj: []byte{
+					0x00, 0x10, 0x02, 0x0a,
+					0x00, 0x00, 0x00, 0x10,
+					0x08, byte('e'), byte('n'), byte('1'),
+					byte('0'), byte('1'), 0x00, 0x00,
+				},
+				ext: &InterfaceInfo{
+					Class: classInterfaceInfo,
+					Type:  0x0a,
+					Interface: &net.Interface{
+						Index: 16,
+						Name:  "en101",
+					},
+				},
+			},
+			// Interface information with ifIndex, IPAddr, name and MTU
+			{
+				proto: iana.ProtocolIPv6ICMP,
+				hdr: []byte{
+					0x20, 0x00, 0x00, 0x00,
+				},
+				obj: []byte{
+					0x00, 0x28, 0x02, 0x0f,
+					0x00, 0x00, 0x00, 0x0f,
+					0x00, 0x02, 0x00, 0x00,
+					0xfe, 0x80, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x00,
+					0x00, 0x00, 0x00, 0x01,
+					0x08, byte('e'), byte('n'), byte('1'),
+					byte('0'), byte('1'), 0x00, 0x00,
+					0x00, 0x00, 0x20, 0x00,
+				},
+				ext: &InterfaceInfo{
+					Class: classInterfaceInfo,
+					Type:  0x0f,
+					Interface: &net.Interface{
+						Index: 15,
+						Name:  "en101",
+						MTU:   8192,
+					},
+					Addr: &net.IPAddr{
+						IP:   net.ParseIP("fe80::1"),
+						Zone: "en101",
+					},
+				},
+			},
+		} {
+			if err := fn(t, et.proto, et.hdr, et.obj, et.ext); err != nil {
+				t.Error(err)
+			}
+		}
+	})
 }
 
 func TestParseInterfaceName(t *testing.T) {
 	ifi := InterfaceInfo{Interface: &net.Interface{}}
-	for i, tt := range parseInterfaceNameTests {
+	for i, tt := range []struct {
+		b []byte
+		error
+	}{
+		{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
+		{[]byte{4, 'e', 'n', '0'}, nil},
+		{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
+		{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
+	} {
 		if _, err := ifi.parseName(tt.b); err != tt.error {
 			t.Errorf("#%d: got %v; want %v", i, err, tt.error)
 		}
diff --git a/icmp/ipv4_test.go b/icmp/ipv4_test.go
index 058953f..3fdee83 100644
--- a/icmp/ipv4_test.go
+++ b/icmp/ipv4_test.go
@@ -15,69 +15,61 @@
 	"golang.org/x/net/ipv4"
 )
 
-type ipv4HeaderTest struct {
-	wireHeaderFromKernel        [ipv4.HeaderLen]byte
-	wireHeaderFromTradBSDKernel [ipv4.HeaderLen]byte
-	Header                      *ipv4.Header
-}
-
-var ipv4HeaderLittleEndianTest = ipv4HeaderTest{
-	// TODO(mikio): Add platform dependent wire header formats when
-	// we support new platforms.
-	wireHeaderFromKernel: [ipv4.HeaderLen]byte{
-		0x45, 0x01, 0xbe, 0xef,
-		0xca, 0xfe, 0x45, 0xdc,
-		0xff, 0x01, 0xde, 0xad,
-		172, 16, 254, 254,
-		192, 168, 0, 1,
-	},
-	wireHeaderFromTradBSDKernel: [ipv4.HeaderLen]byte{
-		0x45, 0x01, 0xef, 0xbe,
-		0xca, 0xfe, 0x45, 0xdc,
-		0xff, 0x01, 0xde, 0xad,
-		172, 16, 254, 254,
-		192, 168, 0, 1,
-	},
-	Header: &ipv4.Header{
-		Version:  ipv4.Version,
-		Len:      ipv4.HeaderLen,
-		TOS:      1,
-		TotalLen: 0xbeef,
-		ID:       0xcafe,
-		Flags:    ipv4.DontFragment,
-		FragOff:  1500,
-		TTL:      255,
-		Protocol: 1,
-		Checksum: 0xdead,
-		Src:      net.IPv4(172, 16, 254, 254),
-		Dst:      net.IPv4(192, 168, 0, 1),
-	},
-}
-
 func TestParseIPv4Header(t *testing.T) {
-	tt := &ipv4HeaderLittleEndianTest
-	if socket.NativeEndian != binary.LittleEndian {
-		t.Skip("no test for non-little endian machine yet")
-	}
-
-	var wh []byte
-	switch runtime.GOOS {
-	case "darwin":
-		wh = tt.wireHeaderFromTradBSDKernel[:]
-	case "freebsd":
-		if freebsdVersion >= 1000000 {
-			wh = tt.wireHeaderFromKernel[:]
-		} else {
-			wh = tt.wireHeaderFromTradBSDKernel[:]
-		}
-	default:
-		wh = tt.wireHeaderFromKernel[:]
-	}
-	h, err := ParseIPv4Header(wh)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !reflect.DeepEqual(h, tt.Header) {
-		t.Fatalf("got %#v; want %#v", h, tt.Header)
+	switch socket.NativeEndian {
+	case binary.LittleEndian:
+		t.Run("LittleEndian", func(t *testing.T) {
+			// TODO(mikio): Add platform dependent wire
+			// header formats when we support new
+			// platforms.
+			wireHeaderFromKernel := [ipv4.HeaderLen]byte{
+				0x45, 0x01, 0xbe, 0xef,
+				0xca, 0xfe, 0x45, 0xdc,
+				0xff, 0x01, 0xde, 0xad,
+				172, 16, 254, 254,
+				192, 168, 0, 1,
+			}
+			wireHeaderFromTradBSDKernel := [ipv4.HeaderLen]byte{
+				0x45, 0x01, 0xef, 0xbe,
+				0xca, 0xfe, 0x45, 0xdc,
+				0xff, 0x01, 0xde, 0xad,
+				172, 16, 254, 254,
+				192, 168, 0, 1,
+			}
+			th := &ipv4.Header{
+				Version:  ipv4.Version,
+				Len:      ipv4.HeaderLen,
+				TOS:      1,
+				TotalLen: 0xbeef,
+				ID:       0xcafe,
+				Flags:    ipv4.DontFragment,
+				FragOff:  1500,
+				TTL:      255,
+				Protocol: 1,
+				Checksum: 0xdead,
+				Src:      net.IPv4(172, 16, 254, 254),
+				Dst:      net.IPv4(192, 168, 0, 1),
+			}
+			var wh []byte
+			switch runtime.GOOS {
+			case "darwin":
+				wh = wireHeaderFromTradBSDKernel[:]
+			case "freebsd":
+				if freebsdVersion >= 1000000 {
+					wh = wireHeaderFromKernel[:]
+				} else {
+					wh = wireHeaderFromTradBSDKernel[:]
+				}
+			default:
+				wh = wireHeaderFromKernel[:]
+			}
+			h, err := ParseIPv4Header(wh)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !reflect.DeepEqual(h, th) {
+				t.Fatalf("got %#v; want %#v", h, th)
+			}
+		})
 	}
 }
diff --git a/icmp/message_test.go b/icmp/message_test.go
index 5d2605f..95f6c37 100644
--- a/icmp/message_test.go
+++ b/icmp/message_test.go
@@ -15,120 +15,117 @@
 	"golang.org/x/net/ipv6"
 )
 
-var marshalAndParseMessageForIPv4Tests = []icmp.Message{
-	{
-		Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
-		Body: &icmp.DstUnreach{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
-		Body: &icmp.TimeExceeded{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv4.ICMPTypeParameterProblem, Code: 2,
-		Body: &icmp.ParamProb{
-			Pointer: 8,
-			Data:    []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv4.ICMPTypeEcho, Code: 0,
-		Body: &icmp.Echo{
-			ID: 1, Seq: 2,
-			Data: []byte("HELLO-R-U-THERE"),
-		},
-	},
-	{
-		Type: ipv4.ICMPTypePhoturis,
-		Body: &icmp.DefaultMessageBody{
-			Data: []byte{0x80, 0x40, 0x20, 0x10},
-		},
-	},
-}
-
-func TestMarshalAndParseMessageForIPv4(t *testing.T) {
-	for i, tt := range marshalAndParseMessageForIPv4Tests {
-		b, err := tt.Marshal(nil)
-		if err != nil {
-			t.Fatal(err)
-		}
-		m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
-		if err != nil {
-			t.Fatal(err)
-		}
-		if m.Type != tt.Type || m.Code != tt.Code {
-			t.Errorf("#%v: got %v; want %v", i, m, &tt)
-		}
-		if !reflect.DeepEqual(m.Body, tt.Body) {
-			t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
-		}
-	}
-}
-
-var marshalAndParseMessageForIPv6Tests = []icmp.Message{
-	{
-		Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
-		Body: &icmp.DstUnreach{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv6.ICMPTypePacketTooBig, Code: 0,
-		Body: &icmp.PacketTooBig{
-			MTU:  1<<16 - 1,
-			Data: []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
-		Body: &icmp.TimeExceeded{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv6.ICMPTypeParameterProblem, Code: 2,
-		Body: &icmp.ParamProb{
-			Pointer: 8,
-			Data:    []byte("ERROR-INVOKING-PACKET"),
-		},
-	},
-	{
-		Type: ipv6.ICMPTypeEchoRequest, Code: 0,
-		Body: &icmp.Echo{
-			ID: 1, Seq: 2,
-			Data: []byte("HELLO-R-U-THERE"),
-		},
-	},
-	{
-		Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
-		Body: &icmp.DefaultMessageBody{
-			Data: []byte{0x80, 0x40, 0x20, 0x10},
-		},
-	},
-}
-
-func TestMarshalAndParseMessageForIPv6(t *testing.T) {
-	pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
-	for i, tt := range marshalAndParseMessageForIPv6Tests {
-		for _, psh := range [][]byte{pshicmp, nil} {
-			b, err := tt.Marshal(psh)
-			if err != nil {
-				t.Fatal(err)
+func TestMarshalAndParseMessage(t *testing.T) {
+	fn := func(t *testing.T, proto int, tms []icmp.Message) {
+		var pshs [][]byte
+		switch proto {
+		case iana.ProtocolICMP:
+			pshs = [][]byte{nil}
+		case iana.ProtocolIPv6ICMP:
+			pshs = [][]byte{
+				icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")),
+				nil,
 			}
-			m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
-			if err != nil {
-				t.Fatal(err)
-			}
-			if m.Type != tt.Type || m.Code != tt.Code {
-				t.Errorf("#%v: got %v; want %v", i, m, &tt)
-			}
-			if !reflect.DeepEqual(m.Body, tt.Body) {
-				t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
+		}
+		for i, tm := range tms {
+			for _, psh := range pshs {
+				b, err := tm.Marshal(psh)
+				if err != nil {
+					t.Fatal(err)
+				}
+				m, err := icmp.ParseMessage(proto, b)
+				if err != nil {
+					t.Fatal(err)
+				}
+				if m.Type != tm.Type || m.Code != tm.Code {
+					t.Errorf("#%d: got %#v; want %#v", i, m, &tm)
+				}
+				if !reflect.DeepEqual(m.Body, tm.Body) {
+					t.Errorf("#%d: got %#v; want %#v", i, m.Body, tm.Body)
+				}
 			}
 		}
 	}
+
+	t.Run("IPv4", func(t *testing.T) {
+		fn(t, iana.ProtocolICMP,
+			[]icmp.Message{
+				{
+					Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
+					Body: &icmp.DstUnreach{
+						Data: []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
+					Body: &icmp.TimeExceeded{
+						Data: []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv4.ICMPTypeParameterProblem, Code: 2,
+					Body: &icmp.ParamProb{
+						Pointer: 8,
+						Data:    []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv4.ICMPTypeEcho, Code: 0,
+					Body: &icmp.Echo{
+						ID: 1, Seq: 2,
+						Data: []byte("HELLO-R-U-THERE"),
+					},
+				},
+				{
+					Type: ipv4.ICMPTypePhoturis,
+					Body: &icmp.DefaultMessageBody{
+						Data: []byte{0x80, 0x40, 0x20, 0x10},
+					},
+				},
+			})
+	})
+	t.Run("IPv6", func(t *testing.T) {
+		fn(t, iana.ProtocolIPv6ICMP,
+			[]icmp.Message{
+				{
+					Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
+					Body: &icmp.DstUnreach{
+						Data: []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv6.ICMPTypePacketTooBig, Code: 0,
+					Body: &icmp.PacketTooBig{
+						MTU:  1<<16 - 1,
+						Data: []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
+					Body: &icmp.TimeExceeded{
+						Data: []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv6.ICMPTypeParameterProblem, Code: 2,
+					Body: &icmp.ParamProb{
+						Pointer: 8,
+						Data:    []byte("ERROR-INVOKING-PACKET"),
+					},
+				},
+				{
+					Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+					Body: &icmp.Echo{
+						ID: 1, Seq: 2,
+						Data: []byte("HELLO-R-U-THERE"),
+					},
+				},
+				{
+					Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
+					Body: &icmp.DefaultMessageBody{
+						Data: []byte{0x80, 0x40, 0x20, 0x10},
+					},
+				},
+			})
+	})
 }
diff --git a/icmp/multipart_test.go b/icmp/multipart_test.go
index 966ccb8..611fecf 100644
--- a/icmp/multipart_test.go
+++ b/icmp/multipart_test.go
@@ -5,6 +5,7 @@
 package icmp_test
 
 import (
+	"errors"
 	"fmt"
 	"net"
 	"reflect"
@@ -16,425 +17,425 @@
 	"golang.org/x/net/ipv6"
 )
 
-var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
-	{
-		Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
-		Body: &icmp.DstUnreach{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{
-					Class: 1,
-					Type:  1,
-					Labels: []icmp.MPLSLabel{
-						{
-							Label: 16014,
-							TC:    0x4,
-							S:     true,
-							TTL:   255,
-						},
-					},
-				},
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x0f,
-					Interface: &net.Interface{
-						Index: 15,
-						Name:  "en101",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP: net.IPv4(192, 168, 0, 1).To4(),
-					},
-				},
-			},
-		},
-	},
-	{
-		Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
-		Body: &icmp.TimeExceeded{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-			Extensions: []icmp.Extension{
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x0f,
-					Interface: &net.Interface{
-						Index: 15,
-						Name:  "en101",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP: net.IPv4(192, 168, 0, 1).To4(),
-					},
-				},
-				&icmp.MPLSLabelStack{
-					Class: 1,
-					Type:  1,
-					Labels: []icmp.MPLSLabel{
-						{
-							Label: 16014,
-							TC:    0x4,
-							S:     true,
-							TTL:   255,
-						},
-					},
-				},
-			},
-		},
-	},
-	{
-		Type: ipv4.ICMPTypeParameterProblem, Code: 2,
-		Body: &icmp.ParamProb{
-			Pointer: 8,
-			Data:    []byte("ERROR-INVOKING-PACKET"),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{
-					Class: 1,
-					Type:  1,
-					Labels: []icmp.MPLSLabel{
-						{
-							Label: 16014,
-							TC:    0x4,
-							S:     true,
-							TTL:   255,
-						},
-					},
-				},
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x0f,
-					Interface: &net.Interface{
-						Index: 15,
-						Name:  "en101",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP: net.IPv4(192, 168, 0, 1).To4(),
-					},
-				},
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x2f,
-					Interface: &net.Interface{
-						Index: 16,
-						Name:  "en102",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP: net.IPv4(192, 168, 0, 2).To4(),
-					},
-				},
-			},
-		},
-	},
-}
-
-func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
-	for i, tt := range marshalAndParseMultipartMessageForIPv4Tests {
-		b, err := tt.Marshal(nil)
+func TestMarshalAndParseMultipartMessage(t *testing.T) {
+	fn := func(t *testing.T, proto int, tm icmp.Message) error {
+		b, err := tm.Marshal(nil)
 		if err != nil {
-			t.Fatal(err)
+			return err
 		}
-		if b[5] != 32 {
-			t.Errorf("#%v: got %v; want 32", i, b[5])
+		switch proto {
+		case iana.ProtocolICMP:
+			if b[5] != 32 {
+				return fmt.Errorf("got %d; want 32", b[5])
+			}
+		case iana.ProtocolIPv6ICMP:
+			if b[4] != 16 {
+				return fmt.Errorf("got %d; want 16", b[4])
+			}
+		default:
+			return fmt.Errorf("unknown protocol: %d", proto)
 		}
-		m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
+		m, err := icmp.ParseMessage(proto, b)
 		if err != nil {
-			t.Fatal(err)
+			return err
 		}
-		if m.Type != tt.Type || m.Code != tt.Code {
-			t.Errorf("#%v: got %v; want %v", i, m, &tt)
+		if m.Type != tm.Type || m.Code != tm.Code {
+			return fmt.Errorf("got %v; want %v", m, &tm)
 		}
 		switch m.Type {
 		case ipv4.ICMPTypeDestinationUnreachable:
-			got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
+			got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
 			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
-				t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+				return errors.New(dumpExtensions(got.Extensions, want.Extensions))
 			}
 			if len(got.Data) != 128 {
-				t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+				return fmt.Errorf("got %d; want 128", len(got.Data))
 			}
 		case ipv4.ICMPTypeTimeExceeded:
-			got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
+			got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
 			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
-				t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+				return errors.New(dumpExtensions(got.Extensions, want.Extensions))
 			}
 			if len(got.Data) != 128 {
-				t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+				return fmt.Errorf("got %d; want 128", len(got.Data))
 			}
 		case ipv4.ICMPTypeParameterProblem:
-			got, want := m.Body.(*icmp.ParamProb), tt.Body.(*icmp.ParamProb)
+			got, want := m.Body.(*icmp.ParamProb), tm.Body.(*icmp.ParamProb)
 			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
-				t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
+				return errors.New(dumpExtensions(got.Extensions, want.Extensions))
 			}
 			if len(got.Data) != 128 {
-				t.Errorf("#%v: got %v; want 128", i, len(got.Data))
+				return fmt.Errorf("got %d; want 128", len(got.Data))
 			}
+		case ipv6.ICMPTypeDestinationUnreachable:
+			got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
+			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+				return errors.New(dumpExtensions(got.Extensions, want.Extensions))
+			}
+			if len(got.Data) != 128 {
+				return fmt.Errorf("got %d; want 128", len(got.Data))
+			}
+		case ipv6.ICMPTypeTimeExceeded:
+			got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
+			if !reflect.DeepEqual(got.Extensions, want.Extensions) {
+				return errors.New(dumpExtensions(got.Extensions, want.Extensions))
+			}
+			if len(got.Data) != 128 {
+				return fmt.Errorf("got %d; want 128", len(got.Data))
+			}
+		default:
+			return fmt.Errorf("unknown message type: %v", m.Type)
 		}
+		return nil
 	}
-}
 
-var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
-	{
-		Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
-		Body: &icmp.DstUnreach{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{
-					Class: 1,
-					Type:  1,
-					Labels: []icmp.MPLSLabel{
-						{
-							Label: 16014,
-							TC:    0x4,
-							S:     true,
-							TTL:   255,
+	t.Run("IPv4", func(t *testing.T) {
+		for i, tm := range []icmp.Message{
+			{
+				Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
+				Body: &icmp.DstUnreach{
+					Data: []byte("ERROR-INVOKING-PACKET"),
+					Extensions: []icmp.Extension{
+						&icmp.MPLSLabelStack{
+							Class: 1,
+							Type:  1,
+							Labels: []icmp.MPLSLabel{
+								{
+									Label: 16014,
+									TC:    0x4,
+									S:     true,
+									TTL:   255,
+								},
+							},
+						},
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x0f,
+							Interface: &net.Interface{
+								Index: 15,
+								Name:  "en101",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP: net.IPv4(192, 168, 0, 1).To4(),
+							},
 						},
 					},
 				},
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x0f,
-					Interface: &net.Interface{
-						Index: 15,
-						Name:  "en101",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP:   net.ParseIP("fe80::1"),
-						Zone: "en101",
-					},
-				},
 			},
-		},
-	},
-	{
-		Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
-		Body: &icmp.TimeExceeded{
-			Data: []byte("ERROR-INVOKING-PACKET"),
-			Extensions: []icmp.Extension{
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x0f,
-					Interface: &net.Interface{
-						Index: 15,
-						Name:  "en101",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP:   net.ParseIP("fe80::1"),
-						Zone: "en101",
-					},
-				},
-				&icmp.MPLSLabelStack{
-					Class: 1,
-					Type:  1,
-					Labels: []icmp.MPLSLabel{
-						{
-							Label: 16014,
-							TC:    0x4,
-							S:     true,
-							TTL:   255,
+			{
+				Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
+				Body: &icmp.TimeExceeded{
+					Data: []byte("ERROR-INVOKING-PACKET"),
+					Extensions: []icmp.Extension{
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x0f,
+							Interface: &net.Interface{
+								Index: 15,
+								Name:  "en101",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP: net.IPv4(192, 168, 0, 1).To4(),
+							},
+						},
+						&icmp.MPLSLabelStack{
+							Class: 1,
+							Type:  1,
+							Labels: []icmp.MPLSLabel{
+								{
+									Label: 16014,
+									TC:    0x4,
+									S:     true,
+									TTL:   255,
+								},
+							},
 						},
 					},
 				},
-				&icmp.InterfaceInfo{
-					Class: 2,
-					Type:  0x2f,
-					Interface: &net.Interface{
-						Index: 16,
-						Name:  "en102",
-						MTU:   8192,
-					},
-					Addr: &net.IPAddr{
-						IP:   net.ParseIP("fe80::1"),
-						Zone: "en102",
+			},
+			{
+				Type: ipv4.ICMPTypeParameterProblem, Code: 2,
+				Body: &icmp.ParamProb{
+					Pointer: 8,
+					Data:    []byte("ERROR-INVOKING-PACKET"),
+					Extensions: []icmp.Extension{
+						&icmp.MPLSLabelStack{
+							Class: 1,
+							Type:  1,
+							Labels: []icmp.MPLSLabel{
+								{
+									Label: 16014,
+									TC:    0x4,
+									S:     true,
+									TTL:   255,
+								},
+							},
+						},
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x0f,
+							Interface: &net.Interface{
+								Index: 15,
+								Name:  "en101",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP: net.IPv4(192, 168, 0, 1).To4(),
+							},
+						},
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x2f,
+							Interface: &net.Interface{
+								Index: 16,
+								Name:  "en102",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP: net.IPv4(192, 168, 0, 2).To4(),
+							},
+						},
 					},
 				},
 			},
-		},
-	},
-}
-
-func TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) {
-	pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
-	for i, tt := range marshalAndParseMultipartMessageForIPv6Tests {
-		for _, psh := range [][]byte{pshicmp, nil} {
-			b, err := tt.Marshal(psh)
-			if err != nil {
-				t.Fatal(err)
-			}
-			if b[4] != 16 {
-				t.Errorf("#%v: got %v; want 16", i, b[4])
-			}
-			m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
-			if err != nil {
-				t.Fatal(err)
-			}
-			if m.Type != tt.Type || m.Code != tt.Code {
-				t.Errorf("#%v: got %v; want %v", i, m, &tt)
-			}
-			switch m.Type {
-			case ipv6.ICMPTypeDestinationUnreachable:
-				got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
-				if !reflect.DeepEqual(got.Extensions, want.Extensions) {
-					t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
-				}
-				if len(got.Data) != 128 {
-					t.Errorf("#%v: got %v; want 128", i, len(got.Data))
-				}
-			case ipv6.ICMPTypeTimeExceeded:
-				got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
-				if !reflect.DeepEqual(got.Extensions, want.Extensions) {
-					t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
-				}
-				if len(got.Data) != 128 {
-					t.Errorf("#%v: got %v; want 128", i, len(got.Data))
-				}
+		} {
+			if err := fn(t, iana.ProtocolICMP, tm); err != nil {
+				t.Errorf("#%d: %v", i, err)
 			}
 		}
-	}
+	})
+	t.Run("IPv6", func(t *testing.T) {
+		for i, tm := range []icmp.Message{
+			{
+				Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
+				Body: &icmp.DstUnreach{
+					Data: []byte("ERROR-INVOKING-PACKET"),
+					Extensions: []icmp.Extension{
+						&icmp.MPLSLabelStack{
+							Class: 1,
+							Type:  1,
+							Labels: []icmp.MPLSLabel{
+								{
+									Label: 16014,
+									TC:    0x4,
+									S:     true,
+									TTL:   255,
+								},
+							},
+						},
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x0f,
+							Interface: &net.Interface{
+								Index: 15,
+								Name:  "en101",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP:   net.ParseIP("fe80::1"),
+								Zone: "en101",
+							},
+						},
+					},
+				},
+			},
+			{
+				Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
+				Body: &icmp.TimeExceeded{
+					Data: []byte("ERROR-INVOKING-PACKET"),
+					Extensions: []icmp.Extension{
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x0f,
+							Interface: &net.Interface{
+								Index: 15,
+								Name:  "en101",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP:   net.ParseIP("fe80::1"),
+								Zone: "en101",
+							},
+						},
+						&icmp.MPLSLabelStack{
+							Class: 1,
+							Type:  1,
+							Labels: []icmp.MPLSLabel{
+								{
+									Label: 16014,
+									TC:    0x4,
+									S:     true,
+									TTL:   255,
+								},
+							},
+						},
+						&icmp.InterfaceInfo{
+							Class: 2,
+							Type:  0x2f,
+							Interface: &net.Interface{
+								Index: 16,
+								Name:  "en102",
+								MTU:   8192,
+							},
+							Addr: &net.IPAddr{
+								IP:   net.ParseIP("fe80::1"),
+								Zone: "en102",
+							},
+						},
+					},
+				},
+			},
+		} {
+			if err := fn(t, iana.ProtocolIPv6ICMP, tm); err != nil {
+				t.Errorf("#%d: %v", i, err)
+			}
+		}
+	})
 }
 
-func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string {
+func dumpExtensions(gotExts, wantExts []icmp.Extension) string {
 	var s string
-	for j, got := range gotExts {
+	for i, got := range gotExts {
 		switch got := got.(type) {
 		case *icmp.MPLSLabelStack:
-			want := wantExts[j].(*icmp.MPLSLabelStack)
+			want := wantExts[i].(*icmp.MPLSLabelStack)
 			if !reflect.DeepEqual(got, want) {
-				s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want)
+				s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
 			}
 		case *icmp.InterfaceInfo:
-			want := wantExts[j].(*icmp.InterfaceInfo)
+			want := wantExts[i].(*icmp.InterfaceInfo)
 			if !reflect.DeepEqual(got, want) {
-				s += fmt.Sprintf("#%v/%v: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, j, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
+				s += fmt.Sprintf("#%d: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
 			}
 		}
 	}
+	if len(s) == 0 {
+		return "<nil>"
+	}
 	return s[:len(s)-1]
 }
 
-var multipartMessageBodyLenTests = []struct {
-	proto int
-	in    icmp.MessageBody
-	out   int
-}{
-	{
-		iana.ProtocolICMP,
-		&icmp.DstUnreach{
-			Data: make([]byte, ipv4.HeaderLen),
-		},
-		4 + ipv4.HeaderLen, // unused and original datagram
-	},
-	{
-		iana.ProtocolICMP,
-		&icmp.TimeExceeded{
-			Data: make([]byte, ipv4.HeaderLen),
-		},
-		4 + ipv4.HeaderLen, // unused and original datagram
-	},
-	{
-		iana.ProtocolICMP,
-		&icmp.ParamProb{
-			Data: make([]byte, ipv4.HeaderLen),
-		},
-		4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
-	},
-
-	{
-		iana.ProtocolICMP,
-		&icmp.ParamProb{
-			Data: make([]byte, ipv4.HeaderLen),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{},
-			},
-		},
-		4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
-	},
-	{
-		iana.ProtocolICMP,
-		&icmp.ParamProb{
-			Data: make([]byte, 128),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{},
-			},
-		},
-		4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
-	},
-	{
-		iana.ProtocolICMP,
-		&icmp.ParamProb{
-			Data: make([]byte, 129),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{},
-			},
-		},
-		4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
-	},
-
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.DstUnreach{
-			Data: make([]byte, ipv6.HeaderLen),
-		},
-		4 + ipv6.HeaderLen, // unused and original datagram
-	},
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.PacketTooBig{
-			Data: make([]byte, ipv6.HeaderLen),
-		},
-		4 + ipv6.HeaderLen, // mtu and original datagram
-	},
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.TimeExceeded{
-			Data: make([]byte, ipv6.HeaderLen),
-		},
-		4 + ipv6.HeaderLen, // unused and original datagram
-	},
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.ParamProb{
-			Data: make([]byte, ipv6.HeaderLen),
-		},
-		4 + ipv6.HeaderLen, // pointer and original datagram
-	},
-
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.DstUnreach{
-			Data: make([]byte, 127),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{},
-			},
-		},
-		4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
-	},
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.DstUnreach{
-			Data: make([]byte, 128),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{},
-			},
-		},
-		4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
-	},
-	{
-		iana.ProtocolIPv6ICMP,
-		&icmp.DstUnreach{
-			Data: make([]byte, 129),
-			Extensions: []icmp.Extension{
-				&icmp.MPLSLabelStack{},
-			},
-		},
-		4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
-	},
-}
-
 func TestMultipartMessageBodyLen(t *testing.T) {
-	for i, tt := range multipartMessageBodyLenTests {
+	for i, tt := range []struct {
+		proto int
+		in    icmp.MessageBody
+		out   int
+	}{
+		{
+			iana.ProtocolICMP,
+			&icmp.DstUnreach{
+				Data: make([]byte, ipv4.HeaderLen),
+			},
+			4 + ipv4.HeaderLen, // unused and original datagram
+		},
+		{
+			iana.ProtocolICMP,
+			&icmp.TimeExceeded{
+				Data: make([]byte, ipv4.HeaderLen),
+			},
+			4 + ipv4.HeaderLen, // unused and original datagram
+		},
+		{
+			iana.ProtocolICMP,
+			&icmp.ParamProb{
+				Data: make([]byte, ipv4.HeaderLen),
+			},
+			4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
+		},
+
+		{
+			iana.ProtocolICMP,
+			&icmp.ParamProb{
+				Data: make([]byte, ipv4.HeaderLen),
+				Extensions: []icmp.Extension{
+					&icmp.MPLSLabelStack{},
+				},
+			},
+			4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
+		},
+		{
+			iana.ProtocolICMP,
+			&icmp.ParamProb{
+				Data: make([]byte, 128),
+				Extensions: []icmp.Extension{
+					&icmp.MPLSLabelStack{},
+				},
+			},
+			4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
+		},
+		{
+			iana.ProtocolICMP,
+			&icmp.ParamProb{
+				Data: make([]byte, 129),
+				Extensions: []icmp.Extension{
+					&icmp.MPLSLabelStack{},
+				},
+			},
+			4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
+		},
+
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.DstUnreach{
+				Data: make([]byte, ipv6.HeaderLen),
+			},
+			4 + ipv6.HeaderLen, // unused and original datagram
+		},
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.PacketTooBig{
+				Data: make([]byte, ipv6.HeaderLen),
+			},
+			4 + ipv6.HeaderLen, // mtu and original datagram
+		},
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.TimeExceeded{
+				Data: make([]byte, ipv6.HeaderLen),
+			},
+			4 + ipv6.HeaderLen, // unused and original datagram
+		},
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.ParamProb{
+				Data: make([]byte, ipv6.HeaderLen),
+			},
+			4 + ipv6.HeaderLen, // pointer and original datagram
+		},
+
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.DstUnreach{
+				Data: make([]byte, 127),
+				Extensions: []icmp.Extension{
+					&icmp.MPLSLabelStack{},
+				},
+			},
+			4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
+		},
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.DstUnreach{
+				Data: make([]byte, 128),
+				Extensions: []icmp.Extension{
+					&icmp.MPLSLabelStack{},
+				},
+			},
+			4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
+		},
+		{
+			iana.ProtocolIPv6ICMP,
+			&icmp.DstUnreach{
+				Data: make([]byte, 129),
+				Extensions: []icmp.Extension{
+					&icmp.MPLSLabelStack{},
+				},
+			},
+			4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
+		},
+	} {
 		if out := tt.in.Len(tt.proto); out != tt.out {
 			t.Errorf("#%d: got %d; want %d", i, out, tt.out)
 		}
diff --git a/icmp/ping_test.go b/icmp/ping_test.go
deleted file mode 100644
index 3171dad..0000000
--- a/icmp/ping_test.go
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package icmp_test
-
-import (
-	"errors"
-	"fmt"
-	"net"
-	"os"
-	"runtime"
-	"sync"
-	"testing"
-	"time"
-
-	"golang.org/x/net/icmp"
-	"golang.org/x/net/internal/iana"
-	"golang.org/x/net/internal/nettest"
-	"golang.org/x/net/ipv4"
-	"golang.org/x/net/ipv6"
-)
-
-func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
-	const host = "www.google.com"
-	ips, err := net.LookupIP(host)
-	if err != nil {
-		return nil, err
-	}
-	netaddr := func(ip net.IP) (net.Addr, error) {
-		switch c.LocalAddr().(type) {
-		case *net.UDPAddr:
-			return &net.UDPAddr{IP: ip}, nil
-		case *net.IPAddr:
-			return &net.IPAddr{IP: ip}, nil
-		default:
-			return nil, errors.New("neither UDPAddr nor IPAddr")
-		}
-	}
-	for _, ip := range ips {
-		switch protocol {
-		case iana.ProtocolICMP:
-			if ip.To4() != nil {
-				return netaddr(ip)
-			}
-		case iana.ProtocolIPv6ICMP:
-			if ip.To16() != nil && ip.To4() == nil {
-				return netaddr(ip)
-			}
-		}
-	}
-	return nil, errors.New("no A or AAAA record")
-}
-
-type pingTest struct {
-	network, address string
-	protocol         int
-	mtype            icmp.Type
-}
-
-var nonPrivilegedPingTests = []pingTest{
-	{"udp4", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
-
-	{"udp6", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
-}
-
-func TestNonPrivilegedPing(t *testing.T) {
-	if testing.Short() {
-		t.Skip("avoid external network")
-	}
-	switch runtime.GOOS {
-	case "darwin":
-	case "linux":
-		t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
-	default:
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
-	for i, tt := range nonPrivilegedPingTests {
-		if err := doPing(tt, i); err != nil {
-			t.Error(err)
-		}
-	}
-}
-
-var privilegedPingTests = []pingTest{
-	{"ip4:icmp", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
-
-	{"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
-}
-
-func TestPrivilegedPing(t *testing.T) {
-	if testing.Short() {
-		t.Skip("avoid external network")
-	}
-	if m, ok := nettest.SupportsRawIPSocket(); !ok {
-		t.Skip(m)
-	}
-
-	for i, tt := range privilegedPingTests {
-		if err := doPing(tt, i); err != nil {
-			t.Error(err)
-		}
-	}
-}
-
-func doPing(tt pingTest, seq int) error {
-	c, err := icmp.ListenPacket(tt.network, tt.address)
-	if err != nil {
-		return err
-	}
-	defer c.Close()
-
-	dst, err := googleAddr(c, tt.protocol)
-	if err != nil {
-		return err
-	}
-
-	if tt.network != "udp6" && tt.protocol == iana.ProtocolIPv6ICMP {
-		var f ipv6.ICMPFilter
-		f.SetAll(true)
-		f.Accept(ipv6.ICMPTypeDestinationUnreachable)
-		f.Accept(ipv6.ICMPTypePacketTooBig)
-		f.Accept(ipv6.ICMPTypeTimeExceeded)
-		f.Accept(ipv6.ICMPTypeParameterProblem)
-		f.Accept(ipv6.ICMPTypeEchoReply)
-		if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
-			return err
-		}
-	}
-
-	wm := icmp.Message{
-		Type: tt.mtype, Code: 0,
-		Body: &icmp.Echo{
-			ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq),
-			Data: []byte("HELLO-R-U-THERE"),
-		},
-	}
-	wb, err := wm.Marshal(nil)
-	if err != nil {
-		return err
-	}
-	if n, err := c.WriteTo(wb, dst); err != nil {
-		return err
-	} else if n != len(wb) {
-		return fmt.Errorf("got %v; want %v", n, len(wb))
-	}
-
-	rb := make([]byte, 1500)
-	if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
-		return err
-	}
-	n, peer, err := c.ReadFrom(rb)
-	if err != nil {
-		return err
-	}
-	rm, err := icmp.ParseMessage(tt.protocol, rb[:n])
-	if err != nil {
-		return err
-	}
-	switch rm.Type {
-	case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
-		return nil
-	default:
-		return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
-	}
-}
-
-func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
-	if testing.Short() {
-		t.Skip("avoid external network")
-	}
-	switch runtime.GOOS {
-	case "darwin":
-	case "linux":
-		t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
-	default:
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
-	network, address := "udp4", "127.0.0.1"
-	if !nettest.SupportsIPv4() {
-		network, address = "udp6", "::1"
-	}
-	const N = 1000
-	var wg sync.WaitGroup
-	wg.Add(N)
-	for i := 0; i < N; i++ {
-		go func() {
-			defer wg.Done()
-			c, err := icmp.ListenPacket(network, address)
-			if err != nil {
-				t.Error(err)
-				return
-			}
-			c.Close()
-		}()
-	}
-	wg.Wait()
-}