ipv4: add support for solaris

This change adds support for Solaris.

To read and write IPv4 ancillary data using ControlMessage, ipv4 pacakge
requires https://go-review.googlesource.com/30171/

Note: Unlike other platforms, Solaris seems to have a few restrictions
on ICMP property access via raw IP sockets. At least applications are
prohibited from using IP_RECVTTL option for ICMP transport.

Fixes golang/go#17323.

Change-Id: Icb6dfa3c8eced28d14693ddfea4601301999d735
Reviewed-on: https://go-review.googlesource.com/30175
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/ipv4/control_pktinfo.go b/ipv4/control_pktinfo.go
index 444782f..90ccee1 100644
--- a/ipv4/control_pktinfo.go
+++ b/ipv4/control_pktinfo.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 darwin linux
+// +build darwin linux solaris
 
 package ipv4
 
diff --git a/ipv4/control_stub.go b/ipv4/control_stub.go
index 5f5a1bd..88602c3 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 nacl plan9 solaris
+// +build nacl plan9
 
 package ipv4
 
diff --git a/ipv4/control_unix.go b/ipv4/control_unix.go
index 6b6682d..668bb69 100644
--- a/ipv4/control_unix.go
+++ b/ipv4/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 darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package ipv4
 
diff --git a/ipv4/defs_solaris.go b/ipv4/defs_solaris.go
index bb74afa..c0b2d3b 100644
--- a/ipv4/defs_solaris.go
+++ b/ipv4/defs_solaris.go
@@ -9,30 +9,24 @@
 package ipv4
 
 /*
+#include <sys/socket.h>
+
 #include <netinet/in.h>
 */
 import "C"
 
 const (
-	sysIP_OPTIONS       = C.IP_OPTIONS
-	sysIP_HDRINCL       = C.IP_HDRINCL
-	sysIP_TOS           = C.IP_TOS
-	sysIP_TTL           = C.IP_TTL
-	sysIP_RECVOPTS      = C.IP_RECVOPTS
-	sysIP_RECVRETOPTS   = C.IP_RECVRETOPTS
-	sysIP_RECVDSTADDR   = C.IP_RECVDSTADDR
-	sysIP_RETOPTS       = C.IP_RETOPTS
-	sysIP_RECVIF        = C.IP_RECVIF
-	sysIP_RECVSLLA      = C.IP_RECVSLLA
-	sysIP_RECVTTL       = C.IP_RECVTTL
-	sysIP_NEXTHOP       = C.IP_NEXTHOP
-	sysIP_PKTINFO       = C.IP_PKTINFO
-	sysIP_RECVPKTINFO   = C.IP_RECVPKTINFO
-	sysIP_DONTFRAG      = C.IP_DONTFRAG
-	sysIP_BOUND_IF      = C.IP_BOUND_IF
-	sysIP_UNSPEC_SRC    = C.IP_UNSPEC_SRC
-	sysIP_BROADCAST_TTL = C.IP_BROADCAST_TTL
-	sysIP_DHCPINIT_IF   = C.IP_DHCPINIT_IF
+	sysIP_OPTIONS     = C.IP_OPTIONS
+	sysIP_HDRINCL     = C.IP_HDRINCL
+	sysIP_TOS         = C.IP_TOS
+	sysIP_TTL         = C.IP_TTL
+	sysIP_RECVOPTS    = C.IP_RECVOPTS
+	sysIP_RECVRETOPTS = C.IP_RECVRETOPTS
+	sysIP_RECVDSTADDR = C.IP_RECVDSTADDR
+	sysIP_RETOPTS     = C.IP_RETOPTS
+	sysIP_RECVIF      = C.IP_RECVIF
+	sysIP_RECVSLLA    = C.IP_RECVSLLA
+	sysIP_RECVTTL     = C.IP_RECVTTL
 
 	sysIP_MULTICAST_IF           = C.IP_MULTICAST_IF
 	sysIP_MULTICAST_TTL          = C.IP_MULTICAST_TTL
@@ -43,15 +37,48 @@
 	sysIP_UNBLOCK_SOURCE         = C.IP_UNBLOCK_SOURCE
 	sysIP_ADD_SOURCE_MEMBERSHIP  = C.IP_ADD_SOURCE_MEMBERSHIP
 	sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP
+	sysIP_NEXTHOP                = C.IP_NEXTHOP
 
-	sysSizeofInetPktinfo = C.sizeof_struct_in_pktinfo
+	sysIP_PKTINFO     = C.IP_PKTINFO
+	sysIP_RECVPKTINFO = C.IP_RECVPKTINFO
+	sysIP_DONTFRAG    = C.IP_DONTFRAG
 
-	sysSizeofIPMreq       = C.sizeof_struct_ip_mreq
-	sysSizeofIPMreqSource = C.sizeof_struct_ip_mreq_source
+	sysIP_BOUND_IF      = C.IP_BOUND_IF
+	sysIP_UNSPEC_SRC    = C.IP_UNSPEC_SRC
+	sysIP_BROADCAST_TTL = C.IP_BROADCAST_TTL
+	sysIP_DHCPINIT_IF   = C.IP_DHCPINIT_IF
+
+	sysIP_REUSEADDR = C.IP_REUSEADDR
+	sysIP_DONTROUTE = C.IP_DONTROUTE
+	sysIP_BROADCAST = C.IP_BROADCAST
+
+	sysMCAST_JOIN_GROUP         = C.MCAST_JOIN_GROUP
+	sysMCAST_LEAVE_GROUP        = C.MCAST_LEAVE_GROUP
+	sysMCAST_BLOCK_SOURCE       = C.MCAST_BLOCK_SOURCE
+	sysMCAST_UNBLOCK_SOURCE     = C.MCAST_UNBLOCK_SOURCE
+	sysMCAST_JOIN_SOURCE_GROUP  = C.MCAST_JOIN_SOURCE_GROUP
+	sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP
+
+	sysSizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage
+	sysSizeofSockaddrInet    = C.sizeof_struct_sockaddr_in
+	sysSizeofInetPktinfo     = C.sizeof_struct_in_pktinfo
+
+	sysSizeofIPMreq         = C.sizeof_struct_ip_mreq
+	sysSizeofIPMreqSource   = C.sizeof_struct_ip_mreq_source
+	sysSizeofGroupReq       = C.sizeof_struct_group_req
+	sysSizeofGroupSourceReq = C.sizeof_struct_group_source_req
 )
 
+type sysSockaddrStorage C.struct_sockaddr_storage
+
+type sysSockaddrInet C.struct_sockaddr_in
+
 type sysInetPktinfo C.struct_in_pktinfo
 
 type sysIPMreq C.struct_ip_mreq
 
 type sysIPMreqSource C.struct_ip_mreq_source
+
+type sysGroupReq C.struct_group_req
+
+type sysGroupSourceReq C.struct_group_source_req
diff --git a/ipv4/dgramopt_posix.go b/ipv4/dgramopt_posix.go
index 40b5e1c..693c137 100644
--- a/ipv4/dgramopt_posix.go
+++ b/ipv4/dgramopt_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 darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
 
 package ipv4
 
diff --git a/ipv4/dgramopt_stub.go b/ipv4/dgramopt_stub.go
index b74df69..53cf1c6 100644
--- a/ipv4/dgramopt_stub.go
+++ b/ipv4/dgramopt_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 nacl plan9 solaris
+// +build nacl plan9
 
 package ipv4
 
diff --git a/ipv4/genericopt_posix.go b/ipv4/genericopt_posix.go
index 53bc79f..f333581 100644
--- a/ipv4/genericopt_posix.go
+++ b/ipv4/genericopt_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 darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
 
 package ipv4
 
diff --git a/ipv4/genericopt_stub.go b/ipv4/genericopt_stub.go
index 1817bad..f907189 100644
--- a/ipv4/genericopt_stub.go
+++ b/ipv4/genericopt_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 nacl plan9 solaris
+// +build nacl plan9
 
 package ipv4
 
diff --git a/ipv4/multicast_test.go b/ipv4/multicast_test.go
index d2bcf85..5a0793c 100644
--- a/ipv4/multicast_test.go
+++ b/ipv4/multicast_test.go
@@ -166,7 +166,11 @@
 		if _, err := p.MulticastLoopback(); err != nil {
 			t.Fatal(err)
 		}
-		cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+		cf := ipv4.FlagDst | ipv4.FlagInterface
+		if runtime.GOOS != "solaris" {
+			// Solaris never allows to modify ICMP properties.
+			cf |= ipv4.FlagTTL
+		}
 
 		for i, toggle := range []bool{true, false, true} {
 			wb, err := (&icmp.Message{
diff --git a/ipv4/multicastlistener_test.go b/ipv4/multicastlistener_test.go
index e342bf1..ccac56a 100644
--- a/ipv4/multicastlistener_test.go
+++ b/ipv4/multicastlistener_test.go
@@ -21,7 +21,7 @@
 
 func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -61,7 +61,7 @@
 
 func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -113,7 +113,7 @@
 
 func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -156,7 +156,7 @@
 
 func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
@@ -201,7 +201,7 @@
 
 func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if testing.Short() {
diff --git a/ipv4/multicastsockopt_test.go b/ipv4/multicastsockopt_test.go
index c76dbe4..d730d31 100644
--- a/ipv4/multicastsockopt_test.go
+++ b/ipv4/multicastsockopt_test.go
@@ -26,7 +26,7 @@
 
 func TestPacketConnMulticastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris":
+	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
@@ -66,7 +66,7 @@
 
 func TestRawConnMulticastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris":
+	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if m, ok := nettest.SupportsRawIPSocket(); !ok {
diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go
index d358fc3..5da0cbe 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 !plan9,!solaris,!windows
+// +build !plan9,!windows
 
 package ipv4
 
diff --git a/ipv4/payload_nocmsg.go b/ipv4/payload_nocmsg.go
index d128c9c..b874de0 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 plan9 solaris windows
+// +build plan9 windows
 
 package ipv4
 
diff --git a/ipv4/readwrite_test.go b/ipv4/readwrite_test.go
index 247d06c..a3d71d1 100644
--- a/ipv4/readwrite_test.go
+++ b/ipv4/readwrite_test.go
@@ -91,7 +91,7 @@
 
 func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 
diff --git a/ipv4/sockopt_asmreq.go b/ipv4/sockopt_asmreq.go
index 4a6aa78..cc55201 100644
--- a/ipv4/sockopt_asmreq.go
+++ b/ipv4/sockopt_asmreq.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 darwin dragonfly freebsd netbsd openbsd windows
+// +build darwin dragonfly freebsd netbsd openbsd solaris windows
 
 package ipv4
 
diff --git a/ipv4/sockopt_asmreq_posix.go b/ipv4/sockopt_asmreq_posix.go
index ef9b139..9207dea 100644
--- a/ipv4/sockopt_asmreq_posix.go
+++ b/ipv4/sockopt_asmreq_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 darwin dragonfly freebsd netbsd openbsd windows
+// +build darwin dragonfly freebsd netbsd openbsd solaris windows
 
 package ipv4
 
diff --git a/ipv4/sockopt_asmreq_stub.go b/ipv4/sockopt_asmreq_stub.go
index 9f7b655..0fbdf47 100644
--- a/ipv4/sockopt_asmreq_stub.go
+++ b/ipv4/sockopt_asmreq_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 !darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!windows
+// +build !darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows
 
 package ipv4
 
diff --git a/ipv4/sockopt_posix.go b/ipv4/sockopt_posix.go
index 0b7d6b6..da68143 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 darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
 
 package ipv4
 
diff --git a/ipv4/sockopt_ssmreq_stub.go b/ipv4/sockopt_ssmreq_stub.go
index e2d98fd..0287396 100644
--- a/ipv4/sockopt_ssmreq_stub.go
+++ b/ipv4/sockopt_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 !darwin,!freebsd,!linux
+// +build !darwin,!freebsd,!linux,!solaris
 
 package ipv4
 
diff --git a/ipv4/sockopt_ssmreq_unix.go b/ipv4/sockopt_ssmreq_unix.go
index 588e9b9..adccd45 100644
--- a/ipv4/sockopt_ssmreq_unix.go
+++ b/ipv4/sockopt_ssmreq_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 darwin freebsd linux
+// +build darwin freebsd linux solaris
 
 package ipv4
 
diff --git a/ipv4/sockopt_stub.go b/ipv4/sockopt_stub.go
index 7cfe57c..c7278c1 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 nacl plan9 solaris
+// +build nacl plan9
 
 package ipv4
 
diff --git a/ipv4/sys_solaris.go b/ipv4/sys_solaris.go
new file mode 100644
index 0000000..fd7532b
--- /dev/null
+++ b/ipv4/sys_solaris.go
@@ -0,0 +1,54 @@
+// Copyright 2016 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"
+)
+
+var (
+	ctlOpts = [ctlMax]ctlOpt{
+		ctlTTL:        {sysIP_RECVTTL, 4, marshalTTL, parseTTL},
+		ctlPacketInfo: {sysIP_PKTINFO, sysSizeofInetPktinfo, marshalPacketInfo, parsePacketInfo},
+	}
+
+	sockOpts = [ssoMax]sockOpt{
+		ssoTOS:                {sysIP_TOS, ssoTypeInt},
+		ssoTTL:                {sysIP_TTL, ssoTypeInt},
+		ssoMulticastTTL:       {sysIP_MULTICAST_TTL, ssoTypeByte},
+		ssoMulticastInterface: {sysIP_MULTICAST_IF, ssoTypeInterface},
+		ssoMulticastLoopback:  {sysIP_MULTICAST_LOOP, ssoTypeByte},
+		ssoReceiveTTL:         {sysIP_RECVTTL, ssoTypeInt},
+		ssoPacketInfo:         {sysIP_RECVPKTINFO, ssoTypeInt},
+		ssoHeaderPrepend:      {sysIP_HDRINCL, ssoTypeInt},
+		ssoJoinGroup:          {sysMCAST_JOIN_GROUP, ssoTypeGroupReq},
+		ssoLeaveGroup:         {sysMCAST_LEAVE_GROUP, ssoTypeGroupReq},
+		ssoJoinSourceGroup:    {sysMCAST_JOIN_SOURCE_GROUP, ssoTypeGroupSourceReq},
+		ssoLeaveSourceGroup:   {sysMCAST_LEAVE_SOURCE_GROUP, ssoTypeGroupSourceReq},
+		ssoBlockSourceGroup:   {sysMCAST_BLOCK_SOURCE, ssoTypeGroupSourceReq},
+		ssoUnblockSourceGroup: {sysMCAST_UNBLOCK_SOURCE, ssoTypeGroupSourceReq},
+	}
+)
+
+func (pi *sysInetPktinfo) setIfindex(i int) {
+	pi.Ifindex = uint32(i)
+}
+
+func (gr *sysGroupReq) setGroup(grp net.IP) {
+	sa := (*sysSockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gr)) + 4))
+	sa.Family = syscall.AF_INET
+	copy(sa.Addr[:], grp)
+}
+
+func (gsr *sysGroupSourceReq) setSourceGroup(grp, src net.IP) {
+	sa := (*sysSockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 4))
+	sa.Family = syscall.AF_INET
+	copy(sa.Addr[:], grp)
+	sa = (*sysSockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 260))
+	sa.Family = syscall.AF_INET
+	copy(sa.Addr[:], src)
+}
diff --git a/ipv4/sys_solaris_amd64.s b/ipv4/sys_solaris_amd64.s
new file mode 100644
index 0000000..39d76af
--- /dev/null
+++ b/ipv4/sys_solaris_amd64.s
@@ -0,0 +1,8 @@
+// Copyright 2016 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 ·sysvicall6(SB),NOSPLIT,$0-88
+	JMP	syscall·sysvicall6(SB)
diff --git a/ipv4/sys_stub.go b/ipv4/sys_stub.go
index c8e55cb..d6dd812 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 nacl plan9 solaris
+// +build nacl plan9
 
 package ipv4
 
diff --git a/ipv4/syscall_solaris.go b/ipv4/syscall_solaris.go
new file mode 100644
index 0000000..3b11288
--- /dev/null
+++ b/ipv4/syscall_solaris.go
@@ -0,0 +1,40 @@
+// Copyright 2016 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 solaris
+
+package ipv4
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+//go:cgo_import_dynamic libc___xnet_getsockopt __xnet_getsockopt "libsocket.so"
+//go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so"
+
+//go:linkname procGetsockopt libc___xnet_getsockopt
+//go:linkname procSetsockopt libc_setsockopt
+
+var (
+	procGetsockopt uintptr
+	procSetsockopt uintptr
+)
+
+func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno)
+
+func getsockopt(s uintptr, level, name int, v unsafe.Pointer, l *uint32) error {
+	_, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procGetsockopt)), 5, s, uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0)
+	if errno != 0 {
+		return error(errno)
+	}
+	return nil
+}
+
+func setsockopt(s uintptr, level, name int, v unsafe.Pointer, l uint32) error {
+	if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procSetsockopt)), 5, s, uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+		return error(errno)
+	}
+	return nil
+}
diff --git a/ipv4/unicast_test.go b/ipv4/unicast_test.go
index 9c632cd..8d57fd0 100644
--- a/ipv4/unicast_test.go
+++ b/ipv4/unicast_test.go
@@ -20,7 +20,7 @@
 
 func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
@@ -74,7 +74,7 @@
 
 func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if m, ok := nettest.SupportsRawIPSocket(); !ok {
@@ -97,7 +97,11 @@
 	}
 	p := ipv4.NewPacketConn(c)
 	defer p.Close()
-	cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface
+	cf := ipv4.FlagDst | ipv4.FlagInterface
+	if runtime.GOOS != "solaris" {
+		// Solaris never allows to modify ICMP properties.
+		cf |= ipv4.FlagTTL
+	}
 
 	for i, toggle := range []bool{true, false, true} {
 		wb, err := (&icmp.Message{
@@ -156,7 +160,7 @@
 
 func TestRawConnReadWriteUnicastICMP(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris", "windows":
+	case "nacl", "plan9", "windows":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if m, ok := nettest.SupportsRawIPSocket(); !ok {
diff --git a/ipv4/unicastsockopt_test.go b/ipv4/unicastsockopt_test.go
index 25606f2..258247b 100644
--- a/ipv4/unicastsockopt_test.go
+++ b/ipv4/unicastsockopt_test.go
@@ -16,7 +16,7 @@
 
 func TestConnUnicastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris":
+	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
@@ -53,7 +53,7 @@
 
 func TestPacketConnUnicastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris":
+	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
@@ -79,7 +79,7 @@
 
 func TestRawConnUnicastSocketOptions(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9", "solaris":
+	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 	if m, ok := nettest.SupportsRawIPSocket(); !ok {
diff --git a/ipv4/zsys_solaris.go b/ipv4/zsys_solaris.go
index d7c2334..a817a19 100644
--- a/ipv4/zsys_solaris.go
+++ b/ipv4/zsys_solaris.go
@@ -6,25 +6,17 @@
 package ipv4
 
 const (
-	sysIP_OPTIONS       = 0x1
-	sysIP_HDRINCL       = 0x2
-	sysIP_TOS           = 0x3
-	sysIP_TTL           = 0x4
-	sysIP_RECVOPTS      = 0x5
-	sysIP_RECVRETOPTS   = 0x6
-	sysIP_RECVDSTADDR   = 0x7
-	sysIP_RETOPTS       = 0x8
-	sysIP_RECVIF        = 0x9
-	sysIP_RECVSLLA      = 0xa
-	sysIP_RECVTTL       = 0xb
-	sysIP_NEXTHOP       = 0x19
-	sysIP_PKTINFO       = 0x1a
-	sysIP_RECVPKTINFO   = 0x1a
-	sysIP_DONTFRAG      = 0x1b
-	sysIP_BOUND_IF      = 0x41
-	sysIP_UNSPEC_SRC    = 0x42
-	sysIP_BROADCAST_TTL = 0x43
-	sysIP_DHCPINIT_IF   = 0x45
+	sysIP_OPTIONS     = 0x1
+	sysIP_HDRINCL     = 0x2
+	sysIP_TOS         = 0x3
+	sysIP_TTL         = 0x4
+	sysIP_RECVOPTS    = 0x5
+	sysIP_RECVRETOPTS = 0x6
+	sysIP_RECVDSTADDR = 0x7
+	sysIP_RETOPTS     = 0x8
+	sysIP_RECVIF      = 0x9
+	sysIP_RECVSLLA    = 0xa
+	sysIP_RECVTTL     = 0xb
 
 	sysIP_MULTICAST_IF           = 0x10
 	sysIP_MULTICAST_TTL          = 0x11
@@ -35,13 +27,52 @@
 	sysIP_UNBLOCK_SOURCE         = 0x16
 	sysIP_ADD_SOURCE_MEMBERSHIP  = 0x17
 	sysIP_DROP_SOURCE_MEMBERSHIP = 0x18
+	sysIP_NEXTHOP                = 0x19
 
-	sysSizeofInetPktinfo = 0xc
+	sysIP_PKTINFO     = 0x1a
+	sysIP_RECVPKTINFO = 0x1a
+	sysIP_DONTFRAG    = 0x1b
 
-	sysSizeofIPMreq       = 0x8
-	sysSizeofIPMreqSource = 0xc
+	sysIP_BOUND_IF      = 0x41
+	sysIP_UNSPEC_SRC    = 0x42
+	sysIP_BROADCAST_TTL = 0x43
+	sysIP_DHCPINIT_IF   = 0x45
+
+	sysIP_REUSEADDR = 0x104
+	sysIP_DONTROUTE = 0x105
+	sysIP_BROADCAST = 0x106
+
+	sysMCAST_JOIN_GROUP         = 0x29
+	sysMCAST_LEAVE_GROUP        = 0x2a
+	sysMCAST_BLOCK_SOURCE       = 0x2b
+	sysMCAST_UNBLOCK_SOURCE     = 0x2c
+	sysMCAST_JOIN_SOURCE_GROUP  = 0x2d
+	sysMCAST_LEAVE_SOURCE_GROUP = 0x2e
+
+	sysSizeofSockaddrStorage = 0x100
+	sysSizeofSockaddrInet    = 0x10
+	sysSizeofInetPktinfo     = 0xc
+
+	sysSizeofIPMreq         = 0x8
+	sysSizeofIPMreqSource   = 0xc
+	sysSizeofGroupReq       = 0x104
+	sysSizeofGroupSourceReq = 0x204
 )
 
+type sysSockaddrStorage struct {
+	Family     uint16
+	X_ss_pad1  [6]int8
+	X_ss_align float64
+	X_ss_pad2  [240]int8
+}
+
+type sysSockaddrInet struct {
+	Family uint16
+	Port   uint16
+	Addr   [4]byte /* in_addr */
+	Zero   [8]int8
+}
+
 type sysInetPktinfo struct {
 	Ifindex  uint32
 	Spec_dst [4]byte /* in_addr */
@@ -58,3 +89,14 @@
 	Sourceaddr [4]byte /* in_addr */
 	Interface  [4]byte /* in_addr */
 }
+
+type sysGroupReq struct {
+	Interface uint32
+	Pad_cgo_0 [256]byte
+}
+
+type sysGroupSourceReq struct {
+	Interface uint32
+	Pad_cgo_0 [256]byte
+	Pad_cgo_1 [256]byte
+}