icmp: make non-privileged tests more adaptive

Assuming every Linux kernel configures net.ipv4.ping_group_range well is
a bit naive.

Change-Id: Ibc2ecbad350238509e3988a1a12eef67c9b475a9
Reviewed-on: https://go-review.googlesource.com/c/net/+/167298
Run-TryBot: Mikio Hara <mikioh.public.networking@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/icmp/diag_test.go b/icmp/diag_test.go
index 176db1d..d2c1342 100644
--- a/icmp/diag_test.go
+++ b/icmp/diag_test.go
@@ -36,13 +36,8 @@
 	}
 
 	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
+		if m, ok := supportsNonPrivilegedICMP(); !ok {
+			t.Skip(m)
 		}
 		for i, dt := range []diagTest{
 			{
@@ -247,12 +242,8 @@
 	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)
+	if m, ok := supportsNonPrivilegedICMP(); !ok {
+		t.Skip(m)
 	}
 
 	network, address := "udp4", "127.0.0.1"
@@ -275,3 +266,34 @@
 	}
 	wg.Wait()
 }
+
+var (
+	nonPrivOnce sync.Once
+	nonPrivMsg  string
+	nonPrivICMP bool
+)
+
+func supportsNonPrivilegedICMP() (string, bool) {
+	nonPrivOnce.Do(func() {
+		switch runtime.GOOS {
+		case "darwin":
+			nonPrivICMP = true
+		case "linux":
+			for _, t := range []struct{ network, address string }{
+				{"udp4", "127.0.0.1"},
+				{"udp6", "::1"},
+			} {
+				c, err := icmp.ListenPacket(t.network, t.address)
+				if err != nil {
+					nonPrivMsg = "you may need to adjust the net.ipv4.ping_group_range kernel state"
+					return
+				}
+				c.Close()
+			}
+			nonPrivICMP = true
+		default:
+			nonPrivMsg = "not supported on " + runtime.GOOS
+		}
+	})
+	return nonPrivMsg, nonPrivICMP
+}