go.net/ipv6: make it work with Go 1.1

This CL makes use of built-in syscall stuff to allow go.net/ipv6
to work together with Go 1.1. Also it's able to improve the package
without churning the Go standard library.

Fixes golang/go#6548.

R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/15040044
diff --git a/ipv6/control_rfc2292_unix.go b/ipv6/control_rfc2292_unix.go
new file mode 100644
index 0000000..eef3968
--- /dev/null
+++ b/ipv6/control_rfc2292_unix.go
@@ -0,0 +1,153 @@
+// 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
+
+package ipv6
+
+import (
+	"net"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+const pktinfo = FlagDst | FlagInterface
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+	opt.Lock()
+	defer opt.Unlock()
+	if cf&FlagHopLimit != 0 {
+		if err := setIPv6ReceiveHopLimit(fd, on); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagHopLimit)
+		} else {
+			opt.clear(FlagHopLimit)
+		}
+	}
+	if cf&pktinfo != 0 {
+		if err := setIPv6ReceivePacketInfo(fd, on); err != nil {
+			return err
+		}
+		if on {
+			opt.set(cf & pktinfo)
+		} else {
+			opt.clear(cf & pktinfo)
+		}
+	}
+	return nil
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	opt.Lock()
+	defer opt.Unlock()
+	l, off := 0, 0
+	if opt.isset(FlagHopLimit) {
+		l += syscall.CmsgSpace(4)
+	}
+	if opt.isset(pktinfo) {
+		l += syscall.CmsgSpace(sysSizeofPacketInfo)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if opt.isset(FlagHopLimit) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = sysSockopt2292HopLimit
+			m.SetLen(syscall.CmsgLen(4))
+			off += syscall.CmsgSpace(4)
+		}
+		if opt.isset(pktinfo) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = sysSockopt2292PacketInfo
+			m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
+			off += syscall.CmsgSpace(sysSizeofPacketInfo)
+		}
+	}
+	return
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+	if len(b) == 0 {
+		return nil, nil
+	}
+	cmsgs, err := syscall.ParseSocketControlMessage(b)
+	if err != nil {
+		return nil, os.NewSyscallError("parse socket control message", err)
+	}
+	cm := &ControlMessage{}
+	for _, m := range cmsgs {
+		if m.Header.Level != ianaProtocolIPv6 {
+			continue
+		}
+		switch m.Header.Type {
+		case sysSockopt2292HopLimit:
+			cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
+		case sysSockopt2292PacketInfo:
+			pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
+			cm.IfIndex = int(pi.IfIndex)
+			cm.Dst = pi.IP[:]
+		}
+	}
+	return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	if cm == nil {
+		return
+	}
+	l, off := 0, 0
+	if cm.HopLimit > 0 {
+		l += syscall.CmsgSpace(4)
+	}
+	pion := false
+	if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
+		pion = true
+		l += syscall.CmsgSpace(sysSizeofPacketInfo)
+	}
+	if len(cm.NextHop) == net.IPv6len {
+		l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if cm.HopLimit > 0 {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = sysSockopt2292HopLimit
+			m.SetLen(syscall.CmsgLen(4))
+			data := oob[off+syscall.CmsgLen(0):]
+			*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
+			off += syscall.CmsgSpace(4)
+		}
+		if pion {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = sysSockopt2292PacketInfo
+			m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
+			pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
+			if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+				copy(pi.IP[:], ip)
+			}
+			if cm.IfIndex != 0 {
+				pi.IfIndex = uint32(cm.IfIndex)
+			}
+			off += syscall.CmsgSpace(sysSizeofPacketInfo)
+		}
+		if len(cm.NextHop) == net.IPv6len {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = sysSockopt2292NextHop
+			m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
+			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
+			sa.Len = syscall.SizeofSockaddrInet6
+			sa.Family = syscall.AF_INET6
+			copy(sa.Addr[:], cm.NextHop)
+			off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+		}
+	}
+	return
+}