go.net/ipv6: implement getsockopt, setsockopt syscalls

This CL implements a part of syscall package that's not included
in Go 1.1 release for not to annoy people who need some package
in go.net sub repository with Go 1.1.

Update golang/go#6548

R=dave, dsymonds, adg
CC=golang-dev
https://golang.org/cl/19940044
diff --git a/ipv6/sys.go b/ipv6/sys.go
new file mode 100644
index 0000000..18b1aca
--- /dev/null
+++ b/ipv6/sys.go
@@ -0,0 +1,23 @@
+// Copyright 2013 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
+
+type sysSockoptLen uint32
+
+const (
+	sysSizeofPacketInfo   = 0x14
+	sysSizeofMulticastReq = 0x14
+	sysSizeofICMPFilter   = 0x20
+)
+
+type sysPacketInfo struct {
+	IP      [16]byte
+	IfIndex uint32
+}
+
+type sysMulticastReq struct {
+	IP      [16]byte
+	IfIndex uint32
+}
diff --git a/ipv6/sys_bsd.go b/ipv6/sys_bsd.go
new file mode 100644
index 0000000..4a08217
--- /dev/null
+++ b/ipv6/sys_bsd.go
@@ -0,0 +1,48 @@
+// Copyright 2013 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 freebsd netbsd openbsd
+
+package ipv6
+
+import (
+	"net"
+	"syscall"
+)
+
+// RFC 3493 options
+const (
+	// See /usr/include/netinet6/in6.h.
+	sysSockoptUnicastHopLimit    = 0x4
+	sysSockoptMulticastHopLimit  = 0xa
+	sysSockoptMulticastInterface = 0x9
+	sysSockoptMulticastLoopback  = 0xb
+	sysSockoptJoinGroup          = 0xc
+	sysSockoptLeaveGroup         = 0xd
+)
+
+// RFC 3542 options
+const (
+	// See /usr/include/netinet6/in6.h.
+	sysSockoptReceiveTrafficClass = 0x39
+	sysSockoptTrafficClass        = 0x3d
+	sysSockoptReceiveHopLimit     = 0x25
+	sysSockoptHopLimit            = 0x2f
+	sysSockoptReceivePacketInfo   = 0x24
+	sysSockoptPacketInfo          = 0x2e
+	sysSockoptReceivePathMTU      = 0x2b
+	sysSockoptPathMTU             = 0x2c
+	sysSockoptNextHop             = 0x30
+	sysSockoptChecksum            = 0x1a
+
+	// See /usr/include/netinet6/in6.h.
+	sysSockoptICMPFilter = 0x12
+)
+
+func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
+	sa.Len = syscall.SizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], ip)
+	sa.Scope_id = uint32(ifindex)
+}
diff --git a/ipv6/sys_darwin.go b/ipv6/sys_darwin.go
new file mode 100644
index 0000000..3d07dff
--- /dev/null
+++ b/ipv6/sys_darwin.go
@@ -0,0 +1,54 @@
+// Copyright 2013 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"
+)
+
+// RFC 2292 options
+const (
+	// See /usr/include/netinet6/in6.h.
+	sysSockopt2292HopLimit   = 0x14
+	sysSockopt2292PacketInfo = 0x13
+	sysSockopt2292NextHop    = 0x15
+)
+
+// RFC 3493 options
+const (
+	// See /usr/include/netinet6/in6.h.
+	sysSockoptUnicastHopLimit    = 0x4
+	sysSockoptMulticastHopLimit  = 0xa
+	sysSockoptMulticastInterface = 0x9
+	sysSockoptMulticastLoopback  = 0xb
+	sysSockoptJoinGroup          = 0xc
+	sysSockoptLeaveGroup         = 0xd
+)
+
+// RFC 3542 options
+const (
+	// See /usr/include/netinet6/in6.h.
+	sysSockoptReceiveTrafficClass = 0x23
+	sysSockoptTrafficClass        = 0x24
+	sysSockoptReceiveHopLimit     = 0x25
+	sysSockoptHopLimit            = 0x2f
+	sysSockoptReceivePacketInfo   = 0x3d
+	sysSockoptPacketInfo          = 0x2e
+	sysSockoptReceivePathMTU      = 0x2b
+	sysSockoptPathMTU             = 0x2c
+	sysSockoptNextHop             = 0x30
+	sysSockoptChecksum            = 0x1a
+
+	// See /usr/include/netinet6/in6.h.
+	sysSockoptICMPFilter = 0x12
+)
+
+func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
+	sa.Len = syscall.SizeofSockaddrInet6
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], ip)
+	sa.Scope_id = uint32(ifindex)
+}
diff --git a/ipv6/sys_linux.go b/ipv6/sys_linux.go
new file mode 100644
index 0000000..d90c8cb
--- /dev/null
+++ b/ipv6/sys_linux.go
@@ -0,0 +1,45 @@
+// Copyright 2013 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"
+)
+
+// RFC 3493 options
+const (
+	// See /usr/include/linux/in6.h.
+	sysSockoptUnicastHopLimit    = 0x10
+	sysSockoptMulticastHopLimit  = 0x12
+	sysSockoptMulticastInterface = 0x11
+	sysSockoptMulticastLoopback  = 0x13
+	sysSockoptJoinGroup          = 0x14
+	sysSockoptLeaveGroup         = 0x15
+)
+
+// RFC 3542 options
+const (
+	// See /usr/include/linux/ipv6.h,in6.h.
+	sysSockoptReceiveTrafficClass = 0x42
+	sysSockoptTrafficClass        = 0x43
+	sysSockoptReceiveHopLimit     = 0x33
+	sysSockoptHopLimit            = 0x34
+	sysSockoptReceivePacketInfo   = 0x31
+	sysSockoptPacketInfo          = 0x32
+	sysSockoptReceivePathMTU      = 0x3c
+	sysSockoptPathMTU             = 0x3d
+	sysSockoptNextHop             = 0x9
+	sysSockoptChecksum            = 0x7
+
+	// See /usr/include/linux/icmpv6.h.
+	sysSockoptICMPFilter = 0x1
+)
+
+func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], ip)
+	sa.Scope_id = uint32(ifindex)
+}
diff --git a/ipv6/sys_unix.go b/ipv6/sys_unix.go
new file mode 100644
index 0000000..40cdfc4
--- /dev/null
+++ b/ipv6/sys_unix.go
@@ -0,0 +1,16 @@
+// Copyright 2013 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 darwin freebsd linux netbsd openbsd
+
+package ipv6
+
+import "syscall"
+
+const sysSizeofMTUInfo = 0x20
+
+type sysMTUInfo struct {
+	Addr syscall.RawSockaddrInet6
+	MTU  uint32
+}
diff --git a/ipv6/sys_windows.go b/ipv6/sys_windows.go
new file mode 100644
index 0000000..c09672f
--- /dev/null
+++ b/ipv6/sys_windows.go
@@ -0,0 +1,33 @@
+// Copyright 2013 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"
+)
+
+// RFC 3493 options
+const (
+	// See ws2tcpip.h.
+	sysSockoptUnicastHopLimit    = 0x4
+	sysSockoptMulticastHopLimit  = 0xa
+	sysSockoptMulticastInterface = 0x9
+	sysSockoptMulticastLoopback  = 0xb
+	sysSockoptJoinGroup          = 0xc
+	sysSockoptLeaveGroup         = 0xd
+)
+
+// RFC 3542 options
+const (
+	// See ws2tcpip.h.
+	sysSockoptPacketInfo = 0x13
+)
+
+func setSockaddr(sa *syscall.RawSockaddrInet6, ip net.IP, ifindex int) {
+	sa.Family = syscall.AF_INET6
+	copy(sa.Addr[:], ip)
+	sa.Scope_id = uint32(ifindex)
+}
diff --git a/ipv6/syscall_linux_386.go b/ipv6/syscall_linux_386.go
new file mode 100644
index 0000000..a386636
--- /dev/null
+++ b/ipv6/syscall_linux_386.go
@@ -0,0 +1,42 @@
+// Copyright 2009 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.
+
+// This code is a duplicate of syscall/syscall_linux_386.go with small
+// modifications.
+
+package ipv6
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+// On x86 Linux, all the socket calls go through an extra indirection,
+// I think because the 5-register system call interface can't handle
+// the 6-argument calls like sendto and recvfrom. Instead the
+// arguments to the underlying system call are the number below and a
+// pointer to an array of uintptr. We hide the pointer in the
+// socketcall assembly to avoid allocation on every system call.
+
+const (
+	// See /usr/include/linux/net.h.
+	_SETSOCKOPT = 14
+	_GETSOCKOPT = 15
+)
+
+var socketcall func(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func getsockopt(fd int, level int, name int, v uintptr, l *sysSockoptLen) error {
+	if _, errno := socketcall(_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+		return error(errno)
+	}
+	return nil
+}
+
+func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error {
+	if _, errno := socketcall(_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), v, l, 0); errno != 0 {
+		return error(errno)
+	}
+	return nil
+}
diff --git a/ipv6/syscall_linux_386.s b/ipv6/syscall_linux_386.s
new file mode 100644
index 0000000..34c0457
--- /dev/null
+++ b/ipv6/syscall_linux_386.s
@@ -0,0 +1,56 @@
+// Copyright 2009 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.
+
+// This code is a duplicate of syscall/syscall_linux_386.s with small
+// modifications.
+
+#define SYS_SOCKETCALL	102	// from zsysnum_linux_386.go
+
+// func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
+// Kernel interface gets call sub-number and pointer to a0 for Go 1.1.
+TEXT ·socketcallnosplit7(SB),7,$0
+	CALL	runtime·entersyscall(SB)
+	MOVL	$SYS_SOCKETCALL, AX	// syscall entry
+	MOVL	4(SP), BX		// socket call number
+	LEAL	8(SP), CX		// pointer to call arguments
+	MOVL	$0, DX
+	MOVL	$0, SI
+	MOVL	$0, DI
+	CALL	*runtime·_vdso(SB)
+	CMPL	AX, $0xfffff001
+	JLS	ok1
+	MOVL	$-1, 32(SP)		// n
+	NEGL	AX
+	MOVL	AX, 36(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok1:
+	MOVL	AX, 32(SP)		// n
+	MOVL	$0, 36(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+// func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)
+// Kernel interface gets call sub-number and pointer to a0 for Go 1.2.
+TEXT ·socketcallnosplit4(SB),4,$0-40
+	CALL	runtime·entersyscall(SB)
+	MOVL	$SYS_SOCKETCALL, AX	// syscall entry
+	MOVL	4(SP), BX		// socket call number
+	LEAL	8(SP), CX		// pointer to call arguments
+	MOVL	$0, DX
+	MOVL	$0, SI
+	MOVL	$0, DI
+	CALL	*runtime·_vdso(SB)
+	CMPL	AX, $0xfffff001
+	JLS	ok2
+	MOVL	$-1, 32(SP)		// n
+	NEGL	AX
+	MOVL	AX, 36(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok2:
+	MOVL	AX, 32(SP)		// n
+	MOVL	$0, 36(SP)		// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
diff --git a/ipv6/syscall_nosplit4_linux_386.go b/ipv6/syscall_nosplit4_linux_386.go
new file mode 100644
index 0000000..6d4ac09
--- /dev/null
+++ b/ipv6/syscall_nosplit4_linux_386.go
@@ -0,0 +1,15 @@
+// Copyright 2013 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 go1.2
+
+package ipv6
+
+import "syscall"
+
+func socketcallnosplit4(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func init() {
+	socketcall = socketcallnosplit4
+}
diff --git a/ipv6/syscall_nosplit7_linux_386.go b/ipv6/syscall_nosplit7_linux_386.go
new file mode 100644
index 0000000..2e4da7b
--- /dev/null
+++ b/ipv6/syscall_nosplit7_linux_386.go
@@ -0,0 +1,15 @@
+// Copyright 2013 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 go1.1,!go1.2
+
+package ipv6
+
+import "syscall"
+
+func socketcallnosplit7(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
+
+func init() {
+	socketcall = socketcallnosplit7
+}
diff --git a/ipv6/syscall_unix.go b/ipv6/syscall_unix.go
new file mode 100644
index 0000000..d88dab5
--- /dev/null
+++ b/ipv6/syscall_unix.go
@@ -0,0 +1,26 @@
+// Copyright 2013 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 darwin freebsd linux,amd64 linux,arm netbsd openbsd
+
+package ipv6
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+func getsockopt(fd int, level, name int, v uintptr, l *sysSockoptLen) error {
+	if _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(unsafe.Pointer(l)), 0); errno != 0 {
+		return error(errno)
+	}
+	return nil
+}
+
+func setsockopt(fd int, level int, name int, v uintptr, l uintptr) error {
+	if _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0); errno != 0 {
+		return error(errno)
+	}
+	return nil
+}