ipv6: in PacketConn tests, retry writes that fail with ENOBUFS

In golang/go#37319, TestPacketConnReadWriteMulticastUDP was observed
to occasionally fail with ENOBUFS on macOS.

This change adds a retry loop for that test function. There are some
other related test functions that may also wrap sendmsg, but I have
not observed any ENOBUFS failures for them — I suspect that some
difference in protocol or traffic class prevents this failure mode,
but we can always add more retry loops if we discover that they are
actually needed.

Fixes golang/go#37319.

Change-Id: I99fce94ff10c6f3c09d493712eba782ec8707a58
Reviewed-on: https://go-review.googlesource.com/c/net/+/369742
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/ipv6/errors_other_test.go b/ipv6/errors_other_test.go
new file mode 100644
index 0000000..5a87d73
--- /dev/null
+++ b/ipv6/errors_other_test.go
@@ -0,0 +1,14 @@
+// Copyright 2021 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.
+
+//go:build !(aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris)
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+
+package ipv6_test
+
+// isENOBUFS reports whether err is unix.ENOBUFS.
+// (Always false on non-Unix platforms.)
+func isENOBUFS(err error) bool {
+	return false
+}
diff --git a/ipv6/errors_unix_test.go b/ipv6/errors_unix_test.go
new file mode 100644
index 0000000..978ae61
--- /dev/null
+++ b/ipv6/errors_unix_test.go
@@ -0,0 +1,20 @@
+// Copyright 2021 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.
+
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package ipv6_test
+
+import (
+	"errors"
+
+	"golang.org/x/sys/unix"
+)
+
+// isENOBUFS reports whether err is unix.ENOBUFS.
+// (Always false on non-Unix platforms.)
+func isENOBUFS(err error) bool {
+	return errors.Is(err, unix.ENOBUFS)
+}
diff --git a/ipv6/multicast_test.go b/ipv6/multicast_test.go
index 7f56927..267098a 100644
--- a/ipv6/multicast_test.go
+++ b/ipv6/multicast_test.go
@@ -10,6 +10,7 @@
 	"os"
 	"runtime"
 	"testing"
+	"time"
 
 	"golang.org/x/net/icmp"
 	"golang.org/x/net/internal/iana"
@@ -100,11 +101,24 @@
 				t.Fatal(err)
 			}
 			cm.HopLimit = i + 1
-			if n, err := p.WriteTo(wb, &cm, &grp); err != nil {
-				t.Fatal(err)
-			} else if n != len(wb) {
-				t.Fatal(err)
+
+			backoff := time.Millisecond
+			for {
+				n, err := p.WriteTo(wb, &cm, &grp)
+				if err != nil {
+					if n == 0 && isENOBUFS(err) {
+						time.Sleep(backoff)
+						backoff *= 2
+						continue
+					}
+					t.Fatal(err)
+				}
+				if n != len(wb) {
+					t.Fatalf("wrote %v bytes; want %v", n, len(wb))
+				}
+				break
 			}
+
 			rb := make([]byte, 128)
 			if n, _, _, err := p.ReadFrom(rb); err != nil {
 				t.Fatal(err)