go.net/ipv4: fix flaky test cases

Fixes golang/go#5709.
Fixes golang/go#5811.

LGTM=dave
R=dave
CC=golang-codereviews
https://golang.org/cl/21360043
diff --git a/ipv4/mocktransponder_test.go b/ipv4/mocktransponder_test.go
index 2fc8ad9..9e1ec6d 100644
--- a/ipv4/mocktransponder_test.go
+++ b/ipv4/mocktransponder_test.go
@@ -5,74 +5,10 @@
 package ipv4_test
 
 import (
-	"code.google.com/p/go.net/ipv4"
 	"net"
 	"testing"
-	"time"
 )
 
-// writeThenReadPayload transmits IPv4 datagram payloads to the
-// loopback address or interface and captures the loopback'd datagram
-// payloads.
-func writeThenReadPayload(t *testing.T, i int, c *ipv4.PacketConn, wb []byte, dst net.Addr) []byte {
-	rb := make([]byte, 1500)
-	c.SetTOS(i + 1)
-	var ip net.IP
-	switch v := dst.(type) {
-	case *net.UDPAddr:
-		ip = v.IP
-	case *net.IPAddr:
-		ip = v.IP
-	}
-	if ip.IsMulticast() {
-		c.SetMulticastTTL(i + 1)
-	} else {
-		c.SetTTL(i + 1)
-	}
-	c.SetDeadline(time.Now().Add(100 * time.Millisecond))
-	if _, err := c.WriteTo(wb, nil, dst); err != nil {
-		t.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err)
-	}
-	n, cm, _, err := c.ReadFrom(rb)
-	if err != nil {
-		t.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err)
-	}
-	t.Logf("rcvd cmsg: %v", cm)
-	return rb[:n]
-}
-
-// writeThenReadDatagram transmits ICMP for IPv4 datagrams to the
-// loopback address or interface and captures the response datagrams
-// from the protocol stack within the kernel.
-func writeThenReadDatagram(t *testing.T, i int, c *ipv4.RawConn, wb []byte, src, dst net.Addr) []byte {
-	rb := make([]byte, ipv4.HeaderLen+len(wb))
-	wh := &ipv4.Header{
-		Version:  ipv4.Version,
-		Len:      ipv4.HeaderLen,
-		TOS:      i + 1,
-		TotalLen: ipv4.HeaderLen + len(wb),
-		TTL:      i + 1,
-		Protocol: 1,
-	}
-	if src != nil {
-		wh.Src = src.(*net.IPAddr).IP
-	}
-	if dst != nil {
-		wh.Dst = dst.(*net.IPAddr).IP
-	}
-	c.SetDeadline(time.Now().Add(100 * time.Millisecond))
-	if err := c.WriteTo(wh, wb, nil); err != nil {
-		t.Fatalf("ipv4.RawConn.WriteTo failed: %v", err)
-	}
-	rh, b, cm, err := c.ReadFrom(rb)
-	if err != nil {
-		t.Fatalf("ipv4.RawConn.ReadFrom failed: %v", err)
-	}
-	t.Logf("rcvd cmsg: %v", cm.String())
-	t.Logf("rcvd hdr: %v", rh.String())
-	return b
-}
-
 func isUnicast(ip net.IP) bool {
 	return ip.To4() != nil && (ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast())
 }
@@ -133,3 +69,14 @@
 	}
 	return nil, false
 }
+
+func acceptor(t *testing.T, ln net.Listener, done chan<- bool) {
+	defer func() { done <- true }()
+
+	c, err := ln.Accept()
+	if err != nil {
+		t.Errorf("net.Listener.Accept failed: %v", err)
+		return
+	}
+	c.Close()
+}
diff --git a/ipv4/multicast_test.go b/ipv4/multicast_test.go
index a12d242..879780c 100644
--- a/ipv4/multicast_test.go
+++ b/ipv4/multicast_test.go
@@ -2,63 +2,93 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux netbsd openbsd
-
 package ipv4_test
 
 import (
 	"code.google.com/p/go.net/ipv4"
 	"net"
 	"os"
+	"runtime"
 	"testing"
+	"time"
 )
 
-func TestReadWriteMulticastIPPayloadUDP(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("to avoid external network")
+func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
 	}
 
-	c, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727
+	c, err := net.ListenPacket("udp4", "224.0.0.0:0") // see RFC 4727
 	if err != nil {
 		t.Fatalf("net.ListenPacket failed: %v", err)
 	}
 	defer c.Close()
 
-	ifi := loopbackInterface()
-	if ifi == nil {
-		t.Skip("an appropriate interface not found")
+	_, port, err := net.SplitHostPort(c.LocalAddr().String())
+	if err != nil {
+		t.Fatalf("net.SplitHostPort failed: %v", err)
 	}
-	dst, err := net.ResolveUDPAddr("udp4", "224.0.0.254:1024") // see RFC 4727
+	dst, err := net.ResolveUDPAddr("udp4", "224.0.0.254:"+port) // see RFC 4727
 	if err != nil {
 		t.Fatalf("net.ResolveUDPAddr failed: %v", err)
 	}
 
 	p := ipv4.NewPacketConn(c)
+	defer p.Close()
 	if err := p.JoinGroup(ifi, dst); err != nil {
 		t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
 	}
 	if err := p.SetMulticastInterface(ifi); err != nil {
 		t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err)
 	}
+	if _, err := p.MulticastInterface(); err != nil {
+		t.Fatalf("ipv4.PacketConn.MulticastInterface failed: %v", err)
+	}
 	if err := p.SetMulticastLoopback(true); err != nil {
 		t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err)
 	}
+	if _, err := p.MulticastLoopback(); err != nil {
+		t.Fatalf("ipv4.PacketConn.MulticastLoopback failed: %v", err)
+	}
 	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
 	for i, toggle := range []bool{true, false, true} {
 		if err := p.SetControlMessage(cf, toggle); err != nil {
 			t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err)
 		}
-		writeThenReadPayload(t, i, p, []byte("HELLO-R-U-THERE"), dst)
+		if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetDeadline failed: %v", err)
+		}
+		p.SetMulticastTTL(i + 1)
+		if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), nil, dst); err != nil {
+			t.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+		if _, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+		}
 	}
 }
 
-func TestReadWriteMulticastIPPayloadICMP(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("to avoid external network")
+func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
 	}
 	if os.Getuid() != 0 {
 		t.Skip("must be root")
 	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
 
 	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
 	if err != nil {
@@ -66,23 +96,30 @@
 	}
 	defer c.Close()
 
-	ifi := loopbackInterface()
-	if ifi == nil {
-		t.Skip("an appropriate interface not found")
-	}
 	dst, err := net.ResolveIPAddr("ip4", "224.0.0.254") // see RFC 4727
 	if err != nil {
 		t.Fatalf("net.ResolveIPAddr failed: %v", err)
 	}
 
 	p := ipv4.NewPacketConn(c)
+	defer p.Close()
 	if err := p.JoinGroup(ifi, dst); err != nil {
 		t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err)
 	}
 	if err := p.SetMulticastInterface(ifi); err != nil {
 		t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err)
 	}
+	if _, err := p.MulticastInterface(); err != nil {
+		t.Fatalf("ipv4.PacketConn.MulticastInterface failed: %v", err)
+	}
+	if err := p.SetMulticastLoopback(true); err != nil {
+		t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err)
+	}
+	if _, err := p.MulticastLoopback(); err != nil {
+		t.Fatalf("ipv4.PacketConn.MulticastLoopback failed: %v", err)
+	}
 	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
 	for i, toggle := range []bool{true, false, true} {
 		wb, err := (&icmpMessage{
 			Type: ipv4.ICMPTypeEcho, Code: 0,
@@ -97,24 +134,43 @@
 		if err := p.SetControlMessage(cf, toggle); err != nil {
 			t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err)
 		}
-		rb := writeThenReadPayload(t, i, p, wb, dst)
-		m, err := parseICMPMessage(rb)
-		if err != nil {
-			t.Fatalf("parseICMPMessage failed: %v", err)
+		if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetDeadline failed: %v", err)
 		}
-		if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
-			t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+		p.SetMulticastTTL(i + 1)
+		if _, err := p.WriteTo(wb, nil, dst); err != nil {
+			t.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+		if n, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+			m, err := parseICMPMessage(b[:n])
+			if err != nil {
+				t.Fatalf("parseICMPMessage failed: %v", err)
+			}
+			switch {
+			case m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1
+			case m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0
+			default:
+				t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+			}
 		}
 	}
 }
 
-func TestReadWriteMulticastIPDatagram(t *testing.T) {
+func TestRawConnReadWriteMulticastICMP(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("to avoid external network")
 	}
 	if os.Getuid() != 0 {
 		t.Skip("must be root")
 	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
 
 	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
 	if err != nil {
@@ -122,10 +178,6 @@
 	}
 	defer c.Close()
 
-	ifi := loopbackInterface()
-	if ifi == nil {
-		t.Skip("an appropriate interface not found")
-	}
 	dst, err := net.ResolveIPAddr("ip4", "224.0.0.254") // see RFC 4727
 	if err != nil {
 		t.Fatalf("ResolveIPAddr failed: %v", err)
@@ -135,13 +187,24 @@
 	if err != nil {
 		t.Fatalf("ipv4.NewRawConn failed: %v", err)
 	}
+	defer r.Close()
 	if err := r.JoinGroup(ifi, dst); err != nil {
 		t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err)
 	}
 	if err := r.SetMulticastInterface(ifi); err != nil {
-		t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err)
+		t.Fatalf("ipv4.RawConn.SetMulticastInterface failed: %v", err)
+	}
+	if _, err := r.MulticastInterface(); err != nil {
+		t.Fatalf("ipv4.RawConn.MulticastInterface failed: %v", err)
+	}
+	if err := r.SetMulticastLoopback(true); err != nil {
+		t.Fatalf("ipv4.RawConn.SetMulticastLoopback failed: %v", err)
+	}
+	if _, err := r.MulticastLoopback(); err != nil {
+		t.Fatalf("ipv4.RawConn.MulticastLoopback failed: %v", err)
 	}
 	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
 	for i, toggle := range []bool{true, false, true} {
 		wb, err := (&icmpMessage{
 			Type: ipv4.ICMPTypeEcho, Code: 0,
@@ -153,16 +216,39 @@
 		if err != nil {
 			t.Fatalf("icmpMessage.Marshal failed: %v", err)
 		}
+		wh := &ipv4.Header{
+			Version:  ipv4.Version,
+			Len:      ipv4.HeaderLen,
+			TOS:      i + 1,
+			TotalLen: ipv4.HeaderLen + len(wb),
+			Protocol: 1,
+			Dst:      dst.IP,
+		}
 		if err := r.SetControlMessage(cf, toggle); err != nil {
 			t.Fatalf("ipv4.RawConn.SetControlMessage failed: %v", err)
 		}
-		rb := writeThenReadDatagram(t, i, r, wb, nil, dst)
-		m, err := parseICMPMessage(rb)
-		if err != nil {
-			t.Fatalf("parseICMPMessage failed: %v", err)
+		if err := r.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.RawConn.SetDeadline failed: %v", err)
 		}
-		if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
-			t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+		r.SetMulticastTTL(i + 1)
+		if err := r.WriteTo(wh, wb, nil); err != nil {
+			t.Fatalf("ipv4.RawConn.WriteTo failed: %v", err)
+		}
+		rb := make([]byte, ipv4.HeaderLen+128)
+		if rh, b, cm, err := r.ReadFrom(rb); err != nil {
+			t.Fatalf("ipv4.RawConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+			m, err := parseICMPMessage(b)
+			if err != nil {
+				t.Fatalf("parseICMPMessage failed: %v", err)
+			}
+			switch {
+			case isUnicast(rh.Dst) && m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1
+			case rh.Dst.IsMulticast() && m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0
+			default:
+				t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+			}
 		}
 	}
 }
diff --git a/ipv4/multicastsockopt_test.go b/ipv4/multicastsockopt_test.go
index 009364d..c476f34 100644
--- a/ipv4/multicastsockopt_test.go
+++ b/ipv4/multicastsockopt_test.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 darwin freebsd linux netbsd openbsd windows
-
 package ipv4_test
 
 import (
@@ -14,8 +12,66 @@
 	"testing"
 )
 
-type testMulticastConn interface {
-	testUnicastConn
+var packetConnMulticastSocketOptionTests = []struct {
+	net, proto, addr string
+	gaddr            net.Addr
+}{
+	{"udp4", "", "224.0.0.0:0", &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}}, // see RFC 4727
+	{"ip4", ":icmp", "0.0.0.0", &net.IPAddr{IP: net.IPv4(224, 0, 0, 250)}},  // see RFC 4727
+}
+
+func TestPacketConnMulticastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	for _, tt := range packetConnMulticastSocketOptionTests {
+		if tt.net == "ip4" && os.Getuid() != 0 {
+			t.Skip("must be root")
+		}
+		c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+		if err != nil {
+			t.Fatalf("net.ListenPacket failed: %v", err)
+		}
+		defer c.Close()
+
+		testMulticastSocketOptions(t, ipv4.NewPacketConn(c), ifi, tt.gaddr)
+	}
+}
+
+func TestRawConnMulticastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	r, err := ipv4.NewRawConn(c)
+	if err != nil {
+		t.Fatalf("ipv4.NewRawConn failed: %v", err)
+	}
+
+	testMulticastSocketOptions(t, r, ifi, &net.IPAddr{IP: net.IPv4(224, 0, 0, 250)}) /// see RFC 4727
+}
+
+type testIPv4MulticastConn interface {
 	MulticastTTL() (int, error)
 	SetMulticastTTL(ttl int) error
 	MulticastLoopback() (bool, error)
@@ -24,103 +80,32 @@
 	LeaveGroup(*net.Interface, net.Addr) error
 }
 
-type multicastSockoptTest struct {
-	tos    int
-	ttl    int
-	mcttl  int
-	mcloop bool
-	gaddr  net.IP
-}
-
-var multicastSockoptTests = []multicastSockoptTest{
-	{DiffServCS0 | NotECNTransport, 127, 128, false, net.IPv4(224, 0, 0, 249)}, // see RFC 4727
-	{DiffServAF11 | NotECNTransport, 255, 254, true, net.IPv4(224, 0, 0, 250)}, // see RFC 4727
-}
-
-func TestUDPMulticastSockopt(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("to avoid external network")
-	}
-
-	for _, tt := range multicastSockoptTests {
-		c, err := net.ListenPacket("udp4", "0.0.0.0:0")
-		if err != nil {
-			t.Fatalf("net.ListenPacket failed: %v", err)
-		}
-		defer c.Close()
-
-		p := ipv4.NewPacketConn(c)
-		testMulticastSockopt(t, tt, p, &net.UDPAddr{IP: tt.gaddr})
-	}
-}
-
-func TestIPMulticastSockopt(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("to avoid external network")
-	}
-	if os.Getuid() != 0 {
-		t.Skip("must be root")
-	}
-
-	for _, tt := range multicastSockoptTests {
-		c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
-		if err != nil {
-			t.Fatalf("net.ListenPacket failed: %v", err)
-		}
-		defer c.Close()
-
-		r, _ := ipv4.NewRawConn(c)
-		testMulticastSockopt(t, tt, r, &net.IPAddr{IP: tt.gaddr})
-	}
-}
-
-func testMulticastSockopt(t *testing.T, tt multicastSockoptTest, c testMulticastConn, gaddr net.Addr) {
-	switch runtime.GOOS {
-	case "windows":
-		// IP_TOS option is supported on Windows 8 and beyond.
-		t.Logf("skipping IP_TOS test on %q", runtime.GOOS)
-	default:
-		if err := c.SetTOS(tt.tos); err != nil {
-			t.Fatalf("ipv4.PacketConn.SetTOS failed: %v", err)
-		}
-		if v, err := c.TOS(); err != nil {
-			t.Fatalf("ipv4.PacketConn.TOS failed: %v", err)
-		} else if v != tt.tos {
-			t.Fatalf("Got unexpected TOS value %v; expected %v", v, tt.tos)
-		}
-	}
-
-	if err := c.SetTTL(tt.ttl); err != nil {
-		t.Fatalf("ipv4.PacketConn.SetTTL failed: %v", err)
-	}
-	if v, err := c.TTL(); err != nil {
-		t.Fatalf("ipv4.PacketConn.TTL failed: %v", err)
-	} else if v != tt.ttl {
-		t.Fatalf("Got unexpected TTL value %v; expected %v", v, tt.ttl)
-	}
-
-	if err := c.SetMulticastTTL(tt.mcttl); err != nil {
+func testMulticastSocketOptions(t *testing.T, c testIPv4MulticastConn, ifi *net.Interface, gaddr net.Addr) {
+	const ttl = 255
+	if err := c.SetMulticastTTL(ttl); err != nil {
 		t.Fatalf("ipv4.PacketConn.SetMulticastTTL failed: %v", err)
 	}
 	if v, err := c.MulticastTTL(); err != nil {
 		t.Fatalf("ipv4.PacketConn.MulticastTTL failed: %v", err)
-	} else if v != tt.mcttl {
-		t.Fatalf("Got unexpected MulticastTTL value %v; expected %v", v, tt.mcttl)
+	} else if v != ttl {
+		t.Fatalf("got unexpected multicast TTL value %v; expected %v", v, ttl)
 	}
 
-	if err := c.SetMulticastLoopback(tt.mcloop); err != nil {
-		t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err)
-	}
-	if v, err := c.MulticastLoopback(); err != nil {
-		t.Fatalf("ipv4.PacketConn.MulticastLoopback failed: %v", err)
-	} else if v != tt.mcloop {
-		t.Fatalf("Got unexpected MulticastLoopback value %v; expected %v", v, tt.mcloop)
+	for _, toggle := range []bool{true, false} {
+		if err := c.SetMulticastLoopback(toggle); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err)
+		}
+		if v, err := c.MulticastLoopback(); err != nil {
+			t.Fatalf("ipv4.PacketConn.MulticastLoopback failed: %v", err)
+		} else if v != toggle {
+			t.Fatalf("got unexpected multicast loopback %v; expected %v", v, toggle)
+		}
 	}
 
-	if err := c.JoinGroup(nil, gaddr); err != nil {
-		t.Fatalf("ipv4.PacketConn.JoinGroup(%v) failed: %v", gaddr, err)
+	if err := c.JoinGroup(ifi, gaddr); err != nil {
+		t.Fatalf("ipv4.PacketConn.JoinGroup(%v, %v) failed: %v", ifi, gaddr, err)
 	}
-	if err := c.LeaveGroup(nil, gaddr); err != nil {
-		t.Fatalf("ipv4.PacketConn.LeaveGroup(%v) failed: %v", gaddr, err)
+	if err := c.LeaveGroup(ifi, gaddr); err != nil {
+		t.Fatalf("ipv4.PacketConn.LeaveGroup(%v, %v) failed: %v", ifi, gaddr, err)
 	}
 }
diff --git a/ipv4/unicast_test.go b/ipv4/unicast_test.go
index 23552ec..9845928 100644
--- a/ipv4/unicast_test.go
+++ b/ipv4/unicast_test.go
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux netbsd openbsd
-
 package ipv4_test
 
 import (
 	"code.google.com/p/go.net/ipv4"
 	"net"
 	"os"
+	"runtime"
 	"testing"
+	"time"
 )
 
 func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
@@ -57,6 +57,7 @@
 	defer c.Close()
 
 	p := ipv4.NewPacketConn(c)
+	defer p.Close()
 	cf := ipv4.FlagTTL | ipv4.FlagInterface
 	if err := p.SetControlMessage(cf, true); err != nil {
 		b.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err)
@@ -83,7 +84,16 @@
 	}
 }
 
-func TestReadWriteUnicastIPPayloadUDP(t *testing.T) {
+func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
 	c, err := net.ListenPacket("udp4", "127.0.0.1:0")
 	if err != nil {
 		t.Fatalf("net.ListenPacket failed: %v", err)
@@ -95,19 +105,44 @@
 		t.Fatalf("net.ResolveUDPAddr failed: %v", err)
 	}
 	p := ipv4.NewPacketConn(c)
+	defer p.Close()
 	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
 	for i, toggle := range []bool{true, false, true} {
 		if err := p.SetControlMessage(cf, toggle); err != nil {
 			t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err)
 		}
-		writeThenReadPayload(t, i, p, []byte("HELLO-R-U-THERE"), dst)
+		p.SetTTL(i + 1)
+		if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetWriteDeadline failed: %v", err)
+		}
+		if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), nil, dst); err != nil {
+			t.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err)
+		}
+		rb := make([]byte, 128)
+		if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetReadDeadline failed: %v", err)
+		}
+		if _, cm, _, err := p.ReadFrom(rb); err != nil {
+			t.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+		}
 	}
 }
 
-func TestReadWriteUnicastIPPayloadICMP(t *testing.T) {
+func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
 	if os.Getuid() != 0 {
 		t.Skip("must be root")
 	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
 
 	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
 	if err != nil {
@@ -120,7 +155,9 @@
 		t.Fatalf("ResolveIPAddr failed: %v", err)
 	}
 	p := ipv4.NewPacketConn(c)
+	defer p.Close()
 	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
 	for i, toggle := range []bool{true, false, true} {
 		wb, err := (&icmpMessage{
 			Type: ipv4.ICMPTypeEcho, Code: 0,
@@ -135,21 +172,49 @@
 		if err := p.SetControlMessage(cf, toggle); err != nil {
 			t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err)
 		}
-		rb := writeThenReadPayload(t, i, p, wb, dst)
-		m, err := parseICMPMessage(rb)
-		if err != nil {
-			t.Fatalf("parseICMPMessage failed: %v", err)
+		p.SetTTL(i + 1)
+		if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetWriteDeadline failed: %v", err)
 		}
-		if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
-			t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+		if _, err := p.WriteTo(wb, nil, dst); err != nil {
+			t.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+	loop:
+		if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.PacketConn.SetReadDeadline failed: %v", err)
+		}
+		if n, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+			m, err := parseICMPMessage(b[:n])
+			if err != nil {
+				t.Fatalf("parseICMPMessage failed: %v", err)
+			}
+			if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
+				// On Linux we must handle own sent packets.
+				goto loop
+			}
+			if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
+				t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+			}
 		}
 	}
 }
 
-func TestReadWriteUnicastIPDatagram(t *testing.T) {
+func TestRawConnReadWriteUnicastICMP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
 	if os.Getuid() != 0 {
 		t.Skip("must be root")
 	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
 
 	c, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
 	if err != nil {
@@ -165,7 +230,9 @@
 	if err != nil {
 		t.Fatalf("ipv4.NewRawConn failed: %v", err)
 	}
+	defer r.Close()
 	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+
 	for i, toggle := range []bool{true, false, true} {
 		wb, err := (&icmpMessage{
 			Type: ipv4.ICMPTypeEcho, Code: 0,
@@ -177,16 +244,44 @@
 		if err != nil {
 			t.Fatalf("icmpMessage.Marshal failed: %v", err)
 		}
+		wh := &ipv4.Header{
+			Version:  ipv4.Version,
+			Len:      ipv4.HeaderLen,
+			TOS:      i + 1,
+			TotalLen: ipv4.HeaderLen + len(wb),
+			TTL:      i + 1,
+			Protocol: 1,
+			Dst:      dst.IP,
+		}
 		if err := r.SetControlMessage(cf, toggle); err != nil {
 			t.Fatalf("ipv4.RawConn.SetControlMessage failed: %v", err)
 		}
-		rb := writeThenReadDatagram(t, i, r, wb, nil, dst)
-		m, err := parseICMPMessage(rb)
-		if err != nil {
-			t.Fatalf("parseICMPMessage failed: %v", err)
+		if err := r.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.RawConn.SetWriteDeadline failed: %v", err)
 		}
-		if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
-			t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+		if err := r.WriteTo(wh, wb, nil); err != nil {
+			t.Fatalf("ipv4.RawConn.WriteTo failed: %v", err)
+		}
+		rb := make([]byte, ipv4.HeaderLen+128)
+	loop:
+		if err := r.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
+			t.Fatalf("ipv4.RawConn.SetReadDeadline failed: %v", err)
+		}
+		if _, b, cm, err := r.ReadFrom(rb); err != nil {
+			t.Fatalf("ipv4.RawConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+			m, err := parseICMPMessage(b)
+			if err != nil {
+				t.Fatalf("parseICMPMessage failed: %v", err)
+			}
+			if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
+				// On Linux we must handle own sent packets.
+				goto loop
+			}
+			if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
+				t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
+			}
 		}
 	}
 }
diff --git a/ipv4/unicastsockopt_test.go b/ipv4/unicastsockopt_test.go
index 3816ab3..3f415f8 100644
--- a/ipv4/unicastsockopt_test.go
+++ b/ipv4/unicastsockopt_test.go
@@ -2,144 +2,134 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin freebsd linux netbsd openbsd windows
-
 package ipv4_test
 
 import (
 	"code.google.com/p/go.net/ipv4"
-	"errors"
 	"net"
 	"os"
 	"runtime"
 	"testing"
 )
 
-type testUnicastConn interface {
+func TestConnUnicastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	ln, err := net.Listen("tcp4", "127.0.0.1:0")
+	if err != nil {
+		t.Fatalf("net.Listen failed: %v", err)
+	}
+	defer ln.Close()
+
+	done := make(chan bool)
+	go acceptor(t, ln, done)
+
+	c, err := net.Dial("tcp4", ln.Addr().String())
+	if err != nil {
+		t.Fatalf("net.Dial failed: %v", err)
+	}
+	defer c.Close()
+
+	testUnicastSocketOptions(t, ipv4.NewConn(c))
+
+	<-done
+}
+
+var packetConnUnicastSocketOptionTests = []struct {
+	net, proto, addr string
+}{
+	{"udp4", "", "127.0.0.1:0"},
+	{"ip4", ":icmp", "127.0.0.1"},
+}
+
+func TestPacketConnUnicastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	for _, tt := range packetConnUnicastSocketOptionTests {
+		if tt.net == "ip4" && os.Getuid() != 0 {
+			t.Skip("must be root")
+		}
+		c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+		if err != nil {
+			t.Fatalf("net.ListenPacket(%q, %q) failed: %v", tt.net+tt.proto, tt.addr, err)
+		}
+		defer c.Close()
+
+		testUnicastSocketOptions(t, ipv4.NewPacketConn(c))
+	}
+}
+
+func TestRawConnUnicastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	c, err := net.ListenPacket("ip4:icmp", "127.0.0.1")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	r, err := ipv4.NewRawConn(c)
+	if err != nil {
+		t.Fatalf("ipv4.NewRawConn failed: %v", err)
+	}
+
+	testUnicastSocketOptions(t, r)
+}
+
+type testIPv4UnicastConn interface {
 	TOS() (int, error)
 	SetTOS(int) error
 	TTL() (int, error)
 	SetTTL(int) error
 }
 
-type unicastSockoptTest struct {
-	tos int
-	ttl int
-}
-
-var unicastSockoptTests = []unicastSockoptTest{
-	{DiffServCS0 | NotECNTransport, 127},
-	{DiffServAF11 | NotECNTransport, 255},
-}
-
-func TestTCPUnicastSockopt(t *testing.T) {
-	for _, tt := range unicastSockoptTests {
-		listener := make(chan net.Listener)
-		go tcpListener(t, "127.0.0.1:0", listener)
-		ln := <-listener
-		if ln == nil {
-			return
-		}
-		defer ln.Close()
-		c, err := net.Dial("tcp4", ln.Addr().String())
-		if err != nil {
-			t.Errorf("net.Dial failed: %v", err)
-			return
-		}
-		defer c.Close()
-
-		cc := ipv4.NewConn(c)
-		if err := testUnicastSockopt(t, tt, cc); err != nil {
-			break
-		}
-	}
-}
-
-func tcpListener(t *testing.T, addr string, listener chan<- net.Listener) {
-	ln, err := net.Listen("tcp4", addr)
-	if err != nil {
-		t.Errorf("net.Listen failed: %v", err)
-		listener <- nil
-		return
-	}
-	listener <- ln
-	c, err := ln.Accept()
-	if err != nil {
-		return
-	}
-	c.Close()
-}
-
-func TestUDPUnicastSockopt(t *testing.T) {
-	for _, tt := range unicastSockoptTests {
-		c, err := net.ListenPacket("udp4", "127.0.0.1:0")
-		if err != nil {
-			t.Errorf("net.ListenPacket failed: %v", err)
-			return
-		}
-		defer c.Close()
-
-		p := ipv4.NewPacketConn(c)
-		if err := testUnicastSockopt(t, tt, p); err != nil {
-			break
-		}
-	}
-}
-
-func TestIPUnicastSockopt(t *testing.T) {
-	if os.Getuid() != 0 {
-		t.Skip("must be root")
-	}
-
-	for _, tt := range unicastSockoptTests {
-		c, err := net.ListenPacket("ip4:icmp", "127.0.0.1")
-		if err != nil {
-			t.Errorf("net.ListenPacket failed: %v", err)
-			return
-		}
-		defer c.Close()
-
-		r, err := ipv4.NewRawConn(c)
-		if err != nil {
-			t.Errorf("ipv4.NewRawConn failed: %v", err)
-			return
-		}
-		if err := testUnicastSockopt(t, tt, r); err != nil {
-			break
-		}
-	}
-}
-
-func testUnicastSockopt(t *testing.T, tt unicastSockoptTest, c testUnicastConn) error {
+func testUnicastSocketOptions(t *testing.T, c testIPv4UnicastConn) {
+	tos := DiffServCS0 | NotECNTransport
 	switch runtime.GOOS {
 	case "windows":
 		// IP_TOS option is supported on Windows 8 and beyond.
-		t.Logf("skipping IP_TOS test on %q", runtime.GOOS)
-	default:
-		if err := c.SetTOS(tt.tos); err != nil {
-			t.Errorf("ipv4.Conn.SetTOS failed: %v", err)
-			return err
-		}
-		if v, err := c.TOS(); err != nil {
-			t.Errorf("ipv4.Conn.TOS failed: %v", err)
-			return err
-		} else if v != tt.tos {
-			t.Errorf("Got unexpected TOS value %v; expected %v", v, tt.tos)
-			return errors.New("Got unexpected TOS value")
-		}
+		t.Skipf("skipping IP_TOS test on %q", runtime.GOOS)
 	}
 
-	if err := c.SetTTL(tt.ttl); err != nil {
-		t.Errorf("ipv4.Conn.SetTTL failed: %v", err)
-		return err
+	if err := c.SetTOS(tos); err != nil {
+		t.Fatalf("ipv4.Conn.SetTOS failed: %v", err)
+	}
+	if v, err := c.TOS(); err != nil {
+		t.Fatalf("ipv4.Conn.TOS failed: %v", err)
+	} else if v != tos {
+		t.Fatalf("got unexpected TOS value %v; expected %v", v, tos)
+	}
+	const ttl = 255
+	if err := c.SetTTL(ttl); err != nil {
+		t.Fatalf("ipv4.Conn.SetTTL failed: %v", err)
 	}
 	if v, err := c.TTL(); err != nil {
-		t.Errorf("ipv4.Conn.TTL failed: %v", err)
-		return err
-	} else if v != tt.ttl {
-		t.Errorf("Got unexpected TTL value %v; expected %v", v, tt.ttl)
-		return errors.New("Got unexpected TTL value")
+		t.Fatalf("ipv4.Conn.TTL failed: %v", err)
+	} else if v != ttl {
+		t.Fatalf("got unexpected TTL value %v; expected %v", v, ttl)
 	}
-
-	return nil
 }