all: add support for zos/s390x

This adds net support for zos/s390x. These changes should not affect other platforms.

Fixes golang/go#42130

Change-Id: Ia7faa29de76b7c5713120657b296106c2e27bfd2
Reviewed-on: https://go-review.googlesource.com/c/net/+/264028
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Trust: Tobias Klauser <tobias.klauser@gmail.com>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
diff --git a/http2/server_test.go b/http2/server_test.go
index 0a50729..7f644d4 100644
--- a/http2/server_test.go
+++ b/http2/server_test.go
@@ -1189,10 +1189,9 @@
 }
 
 func TestServer_RejectsLargeFrames(t *testing.T) {
-	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" || runtime.GOOS == "zos" {
 		t.Skip("see golang.org/issue/13434, golang.org/issue/37321")
 	}
-
 	st := newServerTester(t, nil)
 	defer st.Close()
 	st.greet()
diff --git a/internal/socket/cmsghdr.go b/internal/socket/cmsghdr.go
index 0a73e27..0cde35a 100644
--- a/internal/socket/cmsghdr.go
+++ b/internal/socket/cmsghdr.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package socket
 
diff --git a/internal/socket/cmsghdr_stub.go b/internal/socket/cmsghdr_stub.go
index 8328b7d..83c35ec 100644
--- a/internal/socket/cmsghdr_stub.go
+++ b/internal/socket/cmsghdr_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
 
 package socket
 
diff --git a/internal/socket/cmsghdr_zos_s390x.go b/internal/socket/cmsghdr_zos_s390x.go
new file mode 100644
index 0000000..98be146
--- /dev/null
+++ b/internal/socket/cmsghdr_zos_s390x.go
@@ -0,0 +1,25 @@
+// Copyright 2020 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 socket
+
+import "syscall"
+
+func (h *cmsghdr) set(l, lvl, typ int) {
+	h.Len = int32(l)
+	h.Level = int32(lvl)
+	h.Type = int32(typ)
+}
+
+func controlHeaderLen() int {
+	return syscall.CmsgLen(0)
+}
+
+func controlMessageLen(dataLen int) int {
+	return syscall.CmsgLen(dataLen)
+}
+
+func controlMessageSpace(dataLen int) int {
+	return syscall.CmsgSpace(dataLen)
+}
diff --git a/internal/socket/error_unix.go b/internal/socket/error_unix.go
index f14872d..47f0d6e 100644
--- a/internal/socket/error_unix.go
+++ b/internal/socket/error_unix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package socket
 
diff --git a/internal/socket/iovec_64bit.go b/internal/socket/iovec_64bit.go
index dfeda75..0309e10 100644
--- a/internal/socket/iovec_64bit.go
+++ b/internal/socket/iovec_64bit.go
@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // +build arm64 amd64 ppc64 ppc64le mips64 mips64le riscv64 s390x
-// +build aix darwin dragonfly freebsd linux netbsd openbsd
+// +build aix darwin dragonfly freebsd linux netbsd openbsd zos
 
 package socket
 
diff --git a/internal/socket/iovec_stub.go b/internal/socket/iovec_stub.go
index a746e90..f44d4f5 100644
--- a/internal/socket/iovec_stub.go
+++ b/internal/socket/iovec_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
 
 package socket
 
diff --git a/internal/socket/msghdr_stub.go b/internal/socket/msghdr_stub.go
index 873490a..1a253d2 100644
--- a/internal/socket/msghdr_stub.go
+++ b/internal/socket/msghdr_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
 
 package socket
 
diff --git a/internal/socket/msghdr_zos_s390x.go b/internal/socket/msghdr_zos_s390x.go
new file mode 100644
index 0000000..eb1a99a
--- /dev/null
+++ b/internal/socket/msghdr_zos_s390x.go
@@ -0,0 +1,36 @@
+// Copyright 2020 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 s390x
+// +build zos
+
+package socket
+
+import "unsafe"
+
+func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
+	for i := range vs {
+		vs[i].set(bs[i])
+	}
+	if len(vs) > 0 {
+		h.Iov = &vs[0]
+		h.Iovlen = int32(len(vs))
+	}
+	if len(oob) > 0 {
+		h.Control = (*byte)(unsafe.Pointer(&oob[0]))
+		h.Controllen = uint32(len(oob))
+	}
+	if sa != nil {
+		h.Name = (*byte)(unsafe.Pointer(&sa[0]))
+		h.Namelen = uint32(len(sa))
+	}
+}
+
+func (h *msghdr) controllen() int {
+	return int(h.Controllen)
+}
+
+func (h *msghdr) flags() int {
+	return int(h.Flags)
+}
diff --git a/internal/socket/rawconn_msg.go b/internal/socket/rawconn_msg.go
index d5ae3f8..610b1a1 100644
--- a/internal/socket/rawconn_msg.go
+++ b/internal/socket/rawconn_msg.go
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package socket
 
 import (
 	"os"
+	"runtime"
 	"syscall"
 )
 
@@ -24,7 +25,7 @@
 	var n int
 	fn := func(s uintptr) bool {
 		n, operr = recvmsg(s, &h, flags)
-		if operr == syscall.EAGAIN {
+		if operr == syscall.EAGAIN || (runtime.GOOS == "zos" && operr == syscall.EWOULDBLOCK) {
 			return false
 		}
 		return true
@@ -61,7 +62,7 @@
 	var n int
 	fn := func(s uintptr) bool {
 		n, operr = sendmsg(s, &h, flags)
-		if operr == syscall.EAGAIN {
+		if operr == syscall.EAGAIN || (runtime.GOOS == "zos" && operr == syscall.EWOULDBLOCK) {
 			return false
 		}
 		return true
diff --git a/internal/socket/rawconn_nomsg.go b/internal/socket/rawconn_nomsg.go
index b8cea6f..e51b60d 100644
--- a/internal/socket/rawconn_nomsg.go
+++ b/internal/socket/rawconn_nomsg.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package socket
 
diff --git a/internal/socket/socket_test.go b/internal/socket/socket_test.go
index 038e8e9..8952c22 100644
--- a/internal/socket/socket_test.go
+++ b/internal/socket/socket_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package socket_test
 
diff --git a/internal/socket/sys_const_zos.go b/internal/socket/sys_const_zos.go
new file mode 100644
index 0000000..01b6372
--- /dev/null
+++ b/internal/socket/sys_const_zos.go
@@ -0,0 +1,17 @@
+// Copyright 2020 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 zos
+
+package socket
+
+import "syscall"
+
+const (
+	sysAF_UNSPEC = syscall.AF_UNSPEC
+	sysAF_INET   = syscall.AF_INET
+	sysAF_INET6  = syscall.AF_INET6
+
+	sysSOCK_RAW = syscall.SOCK_RAW
+)
diff --git a/internal/socket/sys_posix.go b/internal/socket/sys_posix.go
index 22eae80..05ded23 100644
--- a/internal/socket/sys_posix.go
+++ b/internal/socket/sys_posix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package socket
 
diff --git a/internal/socket/sys_stub.go b/internal/socket/sys_stub.go
index 8e1e074..3c97008 100644
--- a/internal/socket/sys_stub.go
+++ b/internal/socket/sys_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package socket
 
diff --git a/internal/socket/sys_zos_s390x.go b/internal/socket/sys_zos_s390x.go
new file mode 100644
index 0000000..1e38b92
--- /dev/null
+++ b/internal/socket/sys_zos_s390x.go
@@ -0,0 +1,38 @@
+// Copyright 2020 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 socket
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+func syscall_syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)
+func syscall_syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
+
+func probeProtocolStack() int {
+	return 4 // sizeof(int) on GOOS=zos GOARCH=s390x
+}
+
+func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
+	l := uint32(len(b))
+	_, _, errno := syscall_syscall6(syscall.SYS_GETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
+	return int(l), errnoErr(errno)
+}
+
+func setsockopt(s uintptr, level, name int, b []byte) error {
+	_, _, errno := syscall_syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
+	return errnoErr(errno)
+}
+
+func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, _, errno := syscall_syscall(syscall.SYS___RECVMSG_A, s, uintptr(unsafe.Pointer(h)), uintptr(flags))
+	return int(n), errnoErr(errno)
+}
+
+func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
+	n, _, errno := syscall_syscall(syscall.SYS___SENDMSG_A, s, uintptr(unsafe.Pointer(h)), uintptr(flags))
+	return int(n), errnoErr(errno)
+}
diff --git a/internal/socket/sys_zos_s390x.s b/internal/socket/sys_zos_s390x.s
new file mode 100644
index 0000000..60d5839
--- /dev/null
+++ b/internal/socket/sys_zos_s390x.s
@@ -0,0 +1,11 @@
+// Copyright 2020 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.
+
+#include "textflag.h"
+
+TEXT ·syscall_syscall(SB),NOSPLIT,$0
+        JMP     syscall·_syscall(SB)
+
+TEXT ·syscall_syscall6(SB),NOSPLIT,$0
+        JMP     syscall·_syscall6(SB)
diff --git a/internal/socket/zsys_zos_s390x.go b/internal/socket/zsys_zos_s390x.go
new file mode 100644
index 0000000..514ca37
--- /dev/null
+++ b/internal/socket/zsys_zos_s390x.go
@@ -0,0 +1,32 @@
+// Copyright 2020 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 socket
+
+type iovec struct {
+	Base *byte
+	Len  uint64
+}
+
+type msghdr struct {
+	Name       *byte
+	Iov        *iovec
+	Control    *byte
+	Flags      int32
+	Namelen    uint32
+	Iovlen     int32
+	Controllen uint32
+}
+
+type cmsghdr struct {
+	Len   int32
+	Level int32
+	Type  int32
+}
+
+const (
+	sizeofCmsghdr       = 12
+	sizeofSockaddrInet  = 16
+	sizeofSockaddrInet6 = 28
+)
diff --git a/ipv4/control_stub.go b/ipv4/control_stub.go
index a0c049d..9d82534 100644
--- a/ipv4/control_stub.go
+++ b/ipv4/control_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv4
 
diff --git a/ipv4/control_zos.go b/ipv4/control_zos.go
new file mode 100644
index 0000000..0442000
--- /dev/null
+++ b/ipv4/control_zos.go
@@ -0,0 +1,86 @@
+// Copyright 2020 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 ipv4
+
+import (
+	"net"
+	"unsafe"
+
+	"golang.org/x/net/internal/iana"
+	"golang.org/x/net/internal/socket"
+)
+
+func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
+	m := socket.ControlMessage(b)
+	m.MarshalHeader(iana.ProtocolIP, sysIP_PKTINFO, sizeofInetPktinfo)
+	if cm != nil {
+		pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0]))
+		if ip := cm.Src.To4(); ip != nil {
+			copy(pi.Addr[:], ip)
+		}
+		if cm.IfIndex > 0 {
+			pi.setIfindex(cm.IfIndex)
+		}
+	}
+	return m.Next(sizeofInetPktinfo)
+}
+
+func parsePacketInfo(cm *ControlMessage, b []byte) {
+	pi := (*inetPktinfo)(unsafe.Pointer(&b[0]))
+	cm.IfIndex = int(pi.Ifindex)
+	if len(cm.Dst) < net.IPv4len {
+		cm.Dst = make(net.IP, net.IPv4len)
+	}
+	copy(cm.Dst, pi.Addr[:])
+}
+
+func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
+	opt.Lock()
+	defer opt.Unlock()
+	if so, ok := sockOpts[ssoReceiveTTL]; ok && cf&FlagTTL != 0 {
+		if err := so.SetInt(c, boolint(on)); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagTTL)
+		} else {
+			opt.clear(FlagTTL)
+		}
+	}
+	if so, ok := sockOpts[ssoPacketInfo]; ok {
+		if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
+			if err := so.SetInt(c, boolint(on)); err != nil {
+				return err
+			}
+			if on {
+				opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
+			} else {
+				opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
+			}
+		}
+	} else {
+		if so, ok := sockOpts[ssoReceiveDst]; ok && cf&FlagDst != 0 {
+			if err := so.SetInt(c, boolint(on)); err != nil {
+				return err
+			}
+			if on {
+				opt.set(FlagDst)
+			} else {
+				opt.clear(FlagDst)
+			}
+		}
+		if so, ok := sockOpts[ssoReceiveInterface]; ok && cf&FlagInterface != 0 {
+			if err := so.SetInt(c, boolint(on)); err != nil {
+				return err
+			}
+			if on {
+				opt.set(FlagInterface)
+			} else {
+				opt.clear(FlagInterface)
+			}
+		}
+	}
+	return nil
+}
diff --git a/ipv4/helper_posix_test.go b/ipv4/helper_posix_test.go
index ee66dd2..638eaf2 100644
--- a/ipv4/helper_posix_test.go
+++ b/ipv4/helper_posix_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package ipv4_test
 
diff --git a/ipv4/helper_stub_test.go b/ipv4/helper_stub_test.go
index 4ba0171..03ad2a9 100644
--- a/ipv4/helper_stub_test.go
+++ b/ipv4/helper_stub_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv4_test
 
diff --git a/ipv4/multicast_test.go b/ipv4/multicast_test.go
index 57e7a96..09463bf 100644
--- a/ipv4/multicast_test.go
+++ b/ipv4/multicast_test.go
@@ -30,7 +30,7 @@
 
 func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "illumos", "js", "nacl", "plan9", "solaris", "windows":
+	case "fuchsia", "hurd", "illumos", "js", "nacl", "plan9", "solaris", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
@@ -139,6 +139,11 @@
 		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
 	}
 	ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
+	// Unable to obtain loopback interface on z/OS, so instead we test on any multicast
+	// capable interface.
+	if runtime.GOOS == "zos" {
+		ifi, err = nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast)
+	}
 	if err != nil {
 		t.Skip(err)
 	}
diff --git a/ipv4/multicastlistener_test.go b/ipv4/multicastlistener_test.go
index dc3f199..534ded6 100644
--- a/ipv4/multicastlistener_test.go
+++ b/ipv4/multicastlistener_test.go
@@ -21,7 +21,7 @@
 
 func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -61,7 +61,7 @@
 
 func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -116,7 +116,7 @@
 
 func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -172,7 +172,7 @@
 
 func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -217,7 +217,7 @@
 
 func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
diff --git a/ipv4/multicastsockopt_test.go b/ipv4/multicastsockopt_test.go
index 234c775..b0ddd07 100644
--- a/ipv4/multicastsockopt_test.go
+++ b/ipv4/multicastsockopt_test.go
@@ -26,7 +26,7 @@
 
 func TestPacketConnMulticastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	ifi, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
@@ -66,7 +66,7 @@
 
 func TestRawConnMulticastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if !nettest.SupportsRawSocket() {
diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go
index e761466..7bde689 100644
--- a/ipv4/payload_cmsg.go
+++ b/ipv4/payload_cmsg.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package ipv4
 
diff --git a/ipv4/payload_nocmsg.go b/ipv4/payload_nocmsg.go
index 1116256..251bd0c 100644
--- a/ipv4/payload_nocmsg.go
+++ b/ipv4/payload_nocmsg.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
 
 package ipv4
 
diff --git a/ipv4/sockopt_posix.go b/ipv4/sockopt_posix.go
index dea6451..ef29718 100644
--- a/ipv4/sockopt_posix.go
+++ b/ipv4/sockopt_posix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package ipv4
 
diff --git a/ipv4/sockopt_stub.go b/ipv4/sockopt_stub.go
index 37d4806..fbca526 100644
--- a/ipv4/sockopt_stub.go
+++ b/ipv4/sockopt_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv4
 
diff --git a/ipv4/sys_stub.go b/ipv4/sys_stub.go
index b9c85b3..5555851 100644
--- a/ipv4/sys_stub.go
+++ b/ipv4/sys_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv4
 
diff --git a/ipv4/sys_zos.go b/ipv4/sys_zos.go
new file mode 100644
index 0000000..7426606
--- /dev/null
+++ b/ipv4/sys_zos.go
@@ -0,0 +1,55 @@
+// Copyright 2020 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 ipv4
+
+import (
+	"net"
+	"syscall"
+	"unsafe"
+
+	"golang.org/x/net/internal/iana"
+	"golang.org/x/net/internal/socket"
+)
+
+var (
+	ctlOpts = [ctlMax]ctlOpt{
+		ctlPacketInfo: {sysIP_PKTINFO, sizeofInetPktinfo, marshalPacketInfo, parsePacketInfo},
+	}
+
+	sockOpts = map[int]*sockOpt{
+		ssoMulticastTTL:       {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}},
+		ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}},
+		ssoMulticastLoopback:  {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 1}},
+		ssoPacketInfo:         {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVPKTINFO, Len: 4}},
+		ssoJoinGroup:          {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq},
+		ssoLeaveGroup:         {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq},
+		ssoJoinSourceGroup:    {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+		ssoLeaveSourceGroup:   {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+		ssoBlockSourceGroup:   {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+		ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+	}
+)
+
+func (pi *inetPktinfo) setIfindex(i int) {
+	pi.Ifindex = uint32(i)
+}
+
+func (gr *groupReq) setGroup(grp net.IP) {
+	sa := (*sockaddrInet4)(unsafe.Pointer(&gr.Group))
+	sa.Family = syscall.AF_INET
+	sa.Len = sizeofSockaddrInet4
+	copy(sa.Addr[:], grp)
+}
+
+func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) {
+	sa := (*sockaddrInet4)(unsafe.Pointer(&gsr.Group))
+	sa.Family = syscall.AF_INET
+	sa.Len = sizeofSockaddrInet4
+	copy(sa.Addr[:], grp)
+	sa = (*sockaddrInet4)(unsafe.Pointer(&gsr.Source))
+	sa.Family = syscall.AF_INET
+	sa.Len = sizeofSockaddrInet4
+	copy(sa.Addr[:], src)
+}
diff --git a/ipv4/unicast_test.go b/ipv4/unicast_test.go
index fce18b0..a2b0338 100644
--- a/ipv4/unicast_test.go
+++ b/ipv4/unicast_test.go
@@ -23,7 +23,9 @@
 	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
-	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil {
+	// Skip this check on z/OS since net.Interfaces() does not return loopback, however
+	// this does not affect the test and it will still pass.
+	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil && runtime.GOOS != "zos" {
 		t.Skipf("not available on %s", runtime.GOOS)
 	}
 
@@ -76,7 +78,9 @@
 	if !nettest.SupportsRawSocket() {
 		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
 	}
-	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil {
+	// Skip this check on z/OS since net.Interfaces() does not return loopback, however
+	// this does not affect the test and it will still pass.
+	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil && runtime.GOOS != "zos" {
 		t.Skipf("not available on %s", runtime.GOOS)
 	}
 
diff --git a/ipv4/unicastsockopt_test.go b/ipv4/unicastsockopt_test.go
index 7527bc1..58d653e 100644
--- a/ipv4/unicastsockopt_test.go
+++ b/ipv4/unicastsockopt_test.go
@@ -16,7 +16,7 @@
 
 func TestConnUnicastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil {
@@ -61,7 +61,7 @@
 
 func TestPacketConnUnicastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if _, err := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); err != nil {
@@ -86,7 +86,7 @@
 
 func TestRawConnUnicastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if !nettest.SupportsRawSocket() {
diff --git a/ipv4/zsys_zos_s390x.go b/ipv4/zsys_zos_s390x.go
new file mode 100644
index 0000000..4bbfda0
--- /dev/null
+++ b/ipv4/zsys_zos_s390x.go
@@ -0,0 +1,80 @@
+// Copyright 2020 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.
+
+// Hand edited based on zerrors_zos_s390x.go
+// TODO(Bill O'Farrell): auto-generate.
+
+package ipv4
+
+const (
+	sysIP_ADD_MEMBERSHIP         = 5
+	sysIP_ADD_SOURCE_MEMBERSHIP  = 12
+	sysIP_BLOCK_SOURCE           = 10
+	sysIP_DEFAULT_MULTICAST_LOOP = 1
+	sysIP_DEFAULT_MULTICAST_TTL  = 1
+	sysIP_DROP_MEMBERSHIP        = 6
+	sysIP_DROP_SOURCE_MEMBERSHIP = 13
+	sysIP_MAX_MEMBERSHIPS        = 20
+	sysIP_MULTICAST_IF           = 7
+	sysIP_MULTICAST_LOOP         = 4
+	sysIP_MULTICAST_TTL          = 3
+	sysIP_OPTIONS                = 1
+	sysIP_PKTINFO                = 101
+	sysIP_RECVPKTINFO            = 102
+	sysIP_TOS                    = 2
+	sysIP_UNBLOCK_SOURCE         = 11
+
+	sysMCAST_JOIN_GROUP         = 40
+	sysMCAST_LEAVE_GROUP        = 41
+	sysMCAST_JOIN_SOURCE_GROUP  = 42
+	sysMCAST_LEAVE_SOURCE_GROUP = 43
+	sysMCAST_BLOCK_SOURCE       = 44
+	sysMCAST_UNBLOCK_SOURCE     = 45
+
+	sizeofIPMreq          = 8
+	sizeofSockaddrInet4   = 16
+	sizeofSockaddrStorage = 128
+	sizeofGroupReq        = 136
+	sizeofGroupSourceReq  = 264
+	sizeofInetPktinfo     = 8
+)
+
+type sockaddrInet4 struct {
+	Len    uint8
+	Family uint8
+	Port   uint16
+	Addr   [4]byte
+	Zero   [8]uint8
+}
+
+type inetPktinfo struct {
+	Addr    [4]byte
+	Ifindex uint32
+}
+
+type sockaddrStorage struct {
+	Len      uint8
+	Family   byte
+	ss_pad1  [6]byte
+	ss_align int64
+	ss_pad2  [112]byte
+}
+
+type groupReq struct {
+	Interface uint32
+	reserved  uint32
+	Group     sockaddrStorage
+}
+
+type groupSourceReq struct {
+	Interface uint32
+	reserved  uint32
+	Group     sockaddrStorage
+	Source    sockaddrStorage
+}
+
+type ipMreq struct {
+	Multiaddr [4]byte /* in_addr */
+	Interface [4]byte /* in_addr */
+}
diff --git a/ipv6/control_rfc3542_unix.go b/ipv6/control_rfc3542_unix.go
index 8c221b5..77c449e 100644
--- a/ipv6/control_rfc3542_unix.go
+++ b/ipv6/control_rfc3542_unix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package ipv6
 
diff --git a/ipv6/control_stub.go b/ipv6/control_stub.go
index 1d773cb..e50b886 100644
--- a/ipv6/control_stub.go
+++ b/ipv6/control_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv6
 
diff --git a/ipv6/control_unix.go b/ipv6/control_unix.go
index 0971a00..ec31ca2 100644
--- a/ipv6/control_unix.go
+++ b/ipv6/control_unix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package ipv6
 
diff --git a/ipv6/helper_posix_test.go b/ipv6/helper_posix_test.go
index cc85cea..6f5e78f 100644
--- a/ipv6/helper_posix_test.go
+++ b/ipv6/helper_posix_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package ipv6_test
 
diff --git a/ipv6/helper_stub_test.go b/ipv6/helper_stub_test.go
index cc515d1..066de62 100644
--- a/ipv6/helper_stub_test.go
+++ b/ipv6/helper_stub_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv6_test
 
diff --git a/ipv6/helper_unix_test.go b/ipv6/helper_unix_test.go
index d94d912..e76d1ca 100644
--- a/ipv6/helper_unix_test.go
+++ b/ipv6/helper_unix_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package ipv6_test
 
diff --git a/ipv6/icmp_stub.go b/ipv6/icmp_stub.go
index 370e51a..297e191 100644
--- a/ipv6/icmp_stub.go
+++ b/ipv6/icmp_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv6
 
diff --git a/ipv6/icmp_zos.go b/ipv6/icmp_zos.go
new file mode 100644
index 0000000..ddf8f09
--- /dev/null
+++ b/ipv6/icmp_zos.go
@@ -0,0 +1,29 @@
+// Copyright 2020 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 ipv6
+
+func (f *icmpv6Filter) accept(typ ICMPType) {
+	f.Filt[typ>>5] |= 1 << (uint32(typ) & 31)
+
+}
+
+func (f *icmpv6Filter) block(typ ICMPType) {
+	f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
+
+}
+
+func (f *icmpv6Filter) setAll(block bool) {
+	for i := range f.Filt {
+		if block {
+			f.Filt[i] = 0
+		} else {
+			f.Filt[i] = 1<<32 - 1
+		}
+	}
+}
+
+func (f *icmpv6Filter) willBlock(typ ICMPType) bool {
+	return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
+}
diff --git a/ipv6/multicastlistener_test.go b/ipv6/multicastlistener_test.go
index 66699ab..353327e 100644
--- a/ipv6/multicastlistener_test.go
+++ b/ipv6/multicastlistener_test.go
@@ -61,7 +61,7 @@
 
 func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if !nettest.SupportsIPv6() {
diff --git a/ipv6/payload_cmsg.go b/ipv6/payload_cmsg.go
index 284a042..8baa26e 100644
--- a/ipv6/payload_cmsg.go
+++ b/ipv6/payload_cmsg.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package ipv6
 
diff --git a/ipv6/payload_nocmsg.go b/ipv6/payload_nocmsg.go
index c5a4c96..00c4f58 100644
--- a/ipv6/payload_nocmsg.go
+++ b/ipv6/payload_nocmsg.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
 
 package ipv6
 
diff --git a/ipv6/sockopt_posix.go b/ipv6/sockopt_posix.go
index 824c623..f718792 100644
--- a/ipv6/sockopt_posix.go
+++ b/ipv6/sockopt_posix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
 
 package ipv6
 
diff --git a/ipv6/sockopt_stub.go b/ipv6/sockopt_stub.go
index 0a87a93..d87db6a 100644
--- a/ipv6/sockopt_stub.go
+++ b/ipv6/sockopt_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv6
 
diff --git a/ipv6/sockopt_test.go b/ipv6/sockopt_test.go
index cbba049..8ac322b 100644
--- a/ipv6/sockopt_test.go
+++ b/ipv6/sockopt_test.go
@@ -17,7 +17,7 @@
 
 func TestConnInitiatorPathMTU(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if !nettest.SupportsIPv6() {
@@ -55,7 +55,7 @@
 
 func TestConnResponderPathMTU(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if !nettest.SupportsIPv6() {
diff --git a/ipv6/sys_ssmreq.go b/ipv6/sys_ssmreq.go
index 9b52e97..88d64f1 100644
--- a/ipv6/sys_ssmreq.go
+++ b/ipv6/sys_ssmreq.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin freebsd linux solaris
+// +build aix darwin freebsd linux solaris zos
 
 package ipv6
 
diff --git a/ipv6/sys_ssmreq_stub.go b/ipv6/sys_ssmreq_stub.go
index d5bc110..b070886 100644
--- a/ipv6/sys_ssmreq_stub.go
+++ b/ipv6/sys_ssmreq_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!freebsd,!linux,!solaris
+// +build !aix,!darwin,!freebsd,!linux,!solaris,!zos
 
 package ipv6
 
diff --git a/ipv6/sys_stub.go b/ipv6/sys_stub.go
index 4f252d0..cbe4a02 100644
--- a/ipv6/sys_stub.go
+++ b/ipv6/sys_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package ipv6
 
diff --git a/ipv6/sys_zos.go b/ipv6/sys_zos.go
new file mode 100644
index 0000000..d4567f9
--- /dev/null
+++ b/ipv6/sys_zos.go
@@ -0,0 +1,70 @@
+// Copyright 2020 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 ipv6
+
+import (
+	"net"
+	"syscall"
+	"unsafe"
+
+	"golang.org/x/net/internal/iana"
+	"golang.org/x/net/internal/socket"
+)
+
+var (
+	ctlOpts = [ctlMax]ctlOpt{
+		ctlHopLimit:   {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit},
+		ctlPacketInfo: {sysIPV6_PKTINFO, sizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo},
+		ctlPathMTU:    {sysIPV6_PATHMTU, sizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU},
+	}
+
+	sockOpts = map[int]*sockOpt{
+		ssoTrafficClass:        {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_TCLASS, Len: 4}},
+		ssoHopLimit:            {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}},
+		ssoMulticastInterface:  {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}},
+		ssoMulticastHopLimit:   {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}},
+		ssoMulticastLoopback:   {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}},
+		ssoReceiveTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVTCLASS, Len: 4}},
+		ssoReceiveHopLimit:     {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVHOPLIMIT, Len: 4}},
+		ssoReceivePacketInfo:   {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPKTINFO, Len: 4}},
+		ssoReceivePathMTU:      {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPATHMTU, Len: 4}},
+		ssoChecksum:            {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_CHECKSUM, Len: 4}},
+		ssoICMPFilter:          {Option: socket.Option{Level: iana.ProtocolIPv6ICMP, Name: sysICMP6_FILTER, Len: sizeofICMPv6Filter}},
+		ssoJoinGroup:           {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq},
+		ssoLeaveGroup:          {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq},
+		ssoJoinSourceGroup:     {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+		ssoLeaveSourceGroup:    {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+		ssoBlockSourceGroup:    {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+		ssoUnblockSourceGroup:  {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq},
+	}
+)
+
+func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) {
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], ip)
+	sa.Scope_id = uint32(i)
+}
+
+func (pi *inet6Pktinfo) setIfindex(i int) {
+	pi.Ifindex = uint32(i)
+}
+
+func (gr *groupReq) setGroup(grp net.IP) {
+	sa := (*sockaddrInet6)(unsafe.Pointer(&gr.Group))
+	sa.Family = syscall.AF_INET6
+	sa.Len = sizeofSockaddrInet6
+	copy(sa.Addr[:], grp)
+}
+
+func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) {
+	sa := (*sockaddrInet6)(unsafe.Pointer(&gsr.Group))
+	sa.Family = syscall.AF_INET6
+	sa.Len = sizeofSockaddrInet6
+	copy(sa.Addr[:], grp)
+	sa = (*sockaddrInet6)(unsafe.Pointer(&gsr.Source))
+	sa.Family = syscall.AF_INET6
+	sa.Len = sizeofSockaddrInet6
+	copy(sa.Addr[:], src)
+}
diff --git a/ipv6/unicast_test.go b/ipv6/unicast_test.go
index a7429f4..32c42c9 100644
--- a/ipv6/unicast_test.go
+++ b/ipv6/unicast_test.go
@@ -78,7 +78,7 @@
 
 func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
 	switch runtime.GOOS {
-	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
+	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows", "zos":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if !nettest.SupportsIPv6() {
diff --git a/ipv6/zsys_zos_s390x.go b/ipv6/zsys_zos_s390x.go
new file mode 100644
index 0000000..3f98069
--- /dev/null
+++ b/ipv6/zsys_zos_s390x.go
@@ -0,0 +1,106 @@
+// Copyright 2020 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.
+
+// Hand edited based on zerrors_zos_s390x.go
+// TODO(Bill O'Farrell): auto-generate.
+
+package ipv6
+
+const (
+	sysIPV6_ADDR_PREFERENCES  = 32
+	sysIPV6_CHECKSUM          = 19
+	sysIPV6_DONTFRAG          = 29
+	sysIPV6_DSTOPTS           = 23
+	sysIPV6_HOPLIMIT          = 11
+	sysIPV6_HOPOPTS           = 22
+	sysIPV6_JOIN_GROUP        = 5
+	sysIPV6_LEAVE_GROUP       = 6
+	sysIPV6_MULTICAST_HOPS    = 9
+	sysIPV6_MULTICAST_IF      = 7
+	sysIPV6_MULTICAST_LOOP    = 4
+	sysIPV6_NEXTHOP           = 20
+	sysIPV6_PATHMTU           = 12
+	sysIPV6_PKTINFO           = 13
+	sysIPV6_PREFER_SRC_CGA    = 0x10
+	sysIPV6_PREFER_SRC_COA    = 0x02
+	sysIPV6_PREFER_SRC_HOME   = 0x01
+	sysIPV6_PREFER_SRC_NONCGA = 0x20
+	sysIPV6_PREFER_SRC_PUBLIC = 0x08
+	sysIPV6_PREFER_SRC_TMP    = 0x04
+	sysIPV6_RECVDSTOPTS       = 28
+	sysIPV6_RECVHOPLIMIT      = 14
+	sysIPV6_RECVHOPOPTS       = 26
+	sysIPV6_RECVPATHMTU       = 16
+	sysIPV6_RECVPKTINFO       = 15
+	sysIPV6_RECVRTHDR         = 25
+	sysIPV6_RECVTCLASS        = 31
+	sysIPV6_RTHDR             = 21
+	sysIPV6_RTHDRDSTOPTS      = 24
+	sysIPV6_RTHDR_TYPE_0      = 0
+	sysIPV6_TCLASS            = 30
+	sysIPV6_UNICAST_HOPS      = 3
+	sysIPV6_USE_MIN_MTU       = 18
+	sysIPV6_V6ONLY            = 10
+
+	sysMCAST_JOIN_GROUP         = 40
+	sysMCAST_LEAVE_GROUP        = 41
+	sysMCAST_JOIN_SOURCE_GROUP  = 42
+	sysMCAST_LEAVE_SOURCE_GROUP = 43
+	sysMCAST_BLOCK_SOURCE       = 44
+	sysMCAST_UNBLOCK_SOURCE     = 45
+
+	sysICMP6_FILTER = 0x1
+
+	sizeofSockaddrStorage = 128
+	sizeofICMPv6Filter    = 32
+	sizeofInet6Pktinfo    = 20
+	sizeofIPv6Mtuinfo     = 32
+	sizeofSockaddrInet6   = 28
+	sizeofGroupReq        = 136
+	sizeofGroupSourceReq  = 264
+)
+
+type sockaddrStorage struct {
+	Len      uint8
+	Family   byte
+	ss_pad1  [6]byte
+	ss_align int64
+	ss_pad2  [112]byte
+}
+
+type sockaddrInet6 struct {
+	Len      uint8
+	Family   uint8
+	Port     uint16
+	Flowinfo uint32
+	Addr     [16]byte
+	Scope_id uint32
+}
+
+type inet6Pktinfo struct {
+	Addr    [16]byte
+	Ifindex uint32
+}
+
+type ipv6Mtuinfo struct {
+	Addr sockaddrInet6
+	Mtu  uint32
+}
+
+type groupReq struct {
+	Interface uint32
+	reserved  uint32
+	Group     sockaddrStorage
+}
+
+type groupSourceReq struct {
+	Interface uint32
+	reserved  uint32
+	Group     sockaddrStorage
+	Source    sockaddrStorage
+}
+
+type icmpv6Filter struct {
+	Filt [8]uint32
+}
diff --git a/nettest/nettest.go b/nettest/nettest.go
index 953562f..83ba858 100644
--- a/nettest/nettest.go
+++ b/nettest/nettest.go
@@ -126,7 +126,7 @@
 		}
 	case "unixpacket":
 		switch runtime.GOOS {
-		case "aix", "android", "fuchsia", "hurd", "darwin", "ios", "js", "nacl", "plan9", "windows":
+		case "aix", "android", "fuchsia", "hurd", "darwin", "ios", "js", "nacl", "plan9", "windows", "zos":
 			return false
 		case "netbsd":
 			// It passes on amd64 at least. 386 fails
diff --git a/nettest/nettest_stub.go b/nettest/nettest_stub.go
index 2bb8c05..22c2461 100644
--- a/nettest/nettest_stub.go
+++ b/nettest/nettest_stub.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
 
 package nettest
 
diff --git a/nettest/nettest_unix.go b/nettest/nettest_unix.go
index afff744..c1b1359 100644
--- a/nettest/nettest_unix.go
+++ b/nettest/nettest_unix.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
 
 package nettest
 
diff --git a/webdav/file.go b/webdav/file.go
index 9f53778..3fcc053 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -12,6 +12,7 @@
 	"os"
 	"path"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"sync"
 	"time"
@@ -272,8 +273,14 @@
 	var n *memFSNode
 	if dir == nil {
 		// We're opening the root.
-		if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
-			return nil, os.ErrPermission
+		if runtime.GOOS == "zos" {
+			if flag&os.O_WRONLY != 0 {
+				return nil, os.ErrPermission
+			}
+		} else {
+			if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
+				return nil, os.ErrPermission
+			}
 		}
 		n, frag = &fs.root, "/"