浏览代码

go.net/ipv6: new package

Package ipv6 implements IP-level socket options for the Internet
Protocol version 6. It also provides datagram based network I/O
methods specific to the IPv6 and higher layer protocols.

Fixes golang/go#5538.

R=dave
CC=golang-dev
https://golang.org/cl/9843044
Mikio Hara 12 年之前
父节点
当前提交
cdfc4ce106
共有 48 个文件被更改,包括 4178 次插入0 次删除
  1. 84 0
      ipv6/control.go
  2. 151 0
      ipv6/control_rfc2292_darwin.go
  3. 213 0
      ipv6/control_rfc3542_bsd.go
  4. 217 0
      ipv6/control_rfc3542_linux.go
  5. 27 0
      ipv6/control_rfc3542_plan9.go
  6. 27 0
      ipv6/control_rfc3542_windows.go
  7. 32 0
      ipv6/control_test.go
  8. 96 0
      ipv6/dgramopt_plan9.go
  9. 178 0
      ipv6/dgramopt_posix.go
  10. 193 0
      ipv6/doc.go
  11. 83 0
      ipv6/endpoint.go
  12. 237 0
      ipv6/gen.go
  13. 34 0
      ipv6/genericopt_plan9.go
  14. 60 0
      ipv6/genericopt_posix.go
  15. 196 0
      ipv6/gentest.go
  16. 28 0
      ipv6/helper.go
  17. 22 0
      ipv6/helper_plan9.go
  18. 46 0
      ipv6/helper_unix.go
  19. 45 0
      ipv6/helper_windows.go
  20. 224 0
      ipv6/iana.go
  21. 38 0
      ipv6/iana_test.go
  22. 46 0
      ipv6/icmp.go
  23. 35 0
      ipv6/icmp_bsd.go
  24. 33 0
      ipv6/icmp_linux.go
  25. 22 0
      ipv6/icmp_plan9.go
  26. 81 0
      ipv6/icmp_test.go
  27. 22 0
      ipv6/icmp_windows.go
  28. 112 0
      ipv6/mockicmp_test.go
  29. 110 0
      ipv6/mocktransponder_test.go
  30. 154 0
      ipv6/multicast_test.go
  31. 185 0
      ipv6/multicastlistener_test.go
  32. 73 0
      ipv6/multicastsockopt_test.go
  33. 15 0
      ipv6/payload.go
  34. 70 0
      ipv6/payload_cmsg.go
  35. 41 0
      ipv6/payload_noncmsg.go
  36. 66 0
      ipv6/sockopt_rfc2292_darwin.go
  37. 19 0
      ipv6/sockopt_rfc3493_bsd.go
  38. 17 0
      ipv6/sockopt_rfc3493_linux.go
  39. 114 0
      ipv6/sockopt_rfc3493_unix.go
  40. 116 0
      ipv6/sockopt_rfc3493_windows.go
  41. 44 0
      ipv6/sockopt_rfc3542_bsd.go
  42. 42 0
      ipv6/sockopt_rfc3542_linux.go
  43. 12 0
      ipv6/sockopt_rfc3542_plan9.go
  44. 48 0
      ipv6/sockopt_rfc3542_unix.go
  45. 62 0
      ipv6/sockopt_rfc3542_windows.go
  46. 118 0
      ipv6/sockopt_test.go
  47. 195 0
      ipv6/unicast_test.go
  48. 95 0
      ipv6/unicastsockopt_test.go

+ 84 - 0
ipv6/control.go

@@ -0,0 +1,84 @@
+// 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 (
+	"errors"
+	"fmt"
+	"net"
+	"sync"
+)
+
+var (
+	errNotSupported    = errors.New("not supported")
+	errMissingAddress  = errors.New("missing address")
+	errInvalidConnType = errors.New("invalid conn type")
+	errNoSuchInterface = errors.New("no such interface")
+)
+
+// References:
+//
+// RFC 2292  Advanced Sockets API for IPv6
+//	http://tools.ietf.org/html/rfc2292
+// RFC 2460  Internet Protocol, Version 6 (IPv6) Specification
+//	http://tools.ietf.org/html/rfc2460
+// RFC 3493  Basic Socket Interface Extensions for IPv6
+//	http://tools.ietf.org/html/rfc3493.html
+// RFC 3542  Advanced Sockets Application Program Interface (API) for IPv6
+//	http://tools.ietf.org/html/rfc3542
+//
+// Note that RFC 3542 obsoltes RFC 2292 but OS X Snow Leopard and the
+// former still support RFC 2292 only.  Please be aware that almost
+// all protocol implementations prohibit using a combination of RFC
+// 2292 and RFC 3542 for some practical reasons.
+
+type rawOpt struct {
+	sync.Mutex
+	cflags ControlFlags
+}
+
+func (c *rawOpt) set(f ControlFlags)        { c.cflags |= f }
+func (c *rawOpt) clear(f ControlFlags)      { c.cflags &^= f }
+func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
+
+// A ControlFlags reprensents per packet basis IP-level socket option
+// control flags.
+type ControlFlags uint
+
+const (
+	FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet
+	FlagHopLimit                              // pass the hop limit on the received packet
+	FlagSrc                                   // pass the source address on the received packet
+	FlagDst                                   // pass the destination address on the received packet
+	FlagInterface                             // pass the interface index on the received packet
+	FlagPathMTU                               // pass the path MTU on the received packet path
+)
+
+// A ControlMessage represents per packet basis IP-level socket
+// options.
+type ControlMessage struct {
+	// Receiving socket options: SetControlMessage allows to
+	// receive the options from the protocol stack using ReadFrom
+	// method of PacketConn.
+	//
+	// Specifying socket options: ControlMessage for WriteTo
+	// method of PacketConn allows to send the options to the
+	// protocol stack.
+	//
+	TrafficClass int    // traffic class, must be 1 <= value <= 255 when specifying
+	HopLimit     int    // hop limit, must be 1 <= value <= 255 when specifying
+	Src          net.IP // source address, specifying only
+	Dst          net.IP // destination address, receiving only
+	IfIndex      int    // interface index, must be 1 <= value when specifying
+	NextHop      net.IP // next hop address, specifying only
+	MTU          int    // path MTU, receiving only
+}
+
+func (cm *ControlMessage) String() string {
+	if cm == nil {
+		return "<nil>"
+	}
+	return fmt.Sprintf("tclass: %#x, hoplim: %v, src: %v, dst: %v, ifindex: %v, nexthop: %v, mtu: %v", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU)
+}

+ 151 - 0
ipv6/control_rfc2292_darwin.go

@@ -0,0 +1,151 @@
+// 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"
+	"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(syscall.SizeofInet6Pktinfo)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if opt.isset(FlagHopLimit) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_2292HOPLIMIT
+			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 = syscall.IPV6_2292PKTINFO
+			m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
+			off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+		}
+	}
+	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 syscall.IPV6_2292HOPLIMIT:
+			cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
+		case syscall.IPV6_2292PKTINFO:
+			pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0]))
+			cm.IfIndex = int(pi.Ifindex)
+			cm.Dst = pi.Addr[:]
+		}
+	}
+	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(syscall.SizeofInet6Pktinfo)
+	}
+	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 = syscall.IPV6_2292HOPLIMIT
+			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 = syscall.IPV6_2292PKTINFO
+			m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
+			pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
+			if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+				copy(pi.Addr[:], ip)
+			}
+			if cm.IfIndex != 0 {
+				pi.Ifindex = uint32(cm.IfIndex)
+			}
+			off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+		}
+		if len(cm.NextHop) == net.IPv6len {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_2292NEXTHOP
+			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
+}

+ 213 - 0
ipv6/control_rfc3542_bsd.go

@@ -0,0 +1,213 @@
+// 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"
+	"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&FlagTrafficClass != 0 {
+		if err := setIPv6ReceiveTrafficClass(fd, on); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagTrafficClass)
+		} else {
+			opt.clear(FlagTrafficClass)
+		}
+	}
+	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)
+		}
+	}
+	if cf&FlagPathMTU != 0 {
+		if err := setIPv6ReceivePathMTU(fd, on); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagPathMTU)
+		} else {
+			opt.clear(FlagPathMTU)
+		}
+	}
+	return nil
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	opt.Lock()
+	defer opt.Unlock()
+	l, off := 0, 0
+	if opt.isset(FlagTrafficClass) {
+		l += syscall.CmsgSpace(4)
+	}
+	if opt.isset(FlagHopLimit) {
+		l += syscall.CmsgSpace(4)
+	}
+	if opt.isset(pktinfo) {
+		l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+	}
+	if opt.isset(FlagPathMTU) {
+		l += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if opt.isset(FlagTrafficClass) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_RECVTCLASS
+			m.SetLen(syscall.CmsgLen(4))
+			off += syscall.CmsgSpace(4)
+		}
+		if opt.isset(FlagHopLimit) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_RECVHOPLIMIT
+			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 = syscall.IPV6_RECVPKTINFO
+			m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
+			off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+		}
+		if opt.isset(FlagPathMTU) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_RECVPATHMTU
+			m.SetLen(syscall.CmsgLen(syscall.SizeofIPv6MTUInfo))
+			off += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
+		}
+	}
+	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 syscall.IPV6_TCLASS:
+			cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
+		case syscall.IPV6_HOPLIMIT:
+			cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
+		case syscall.IPV6_PKTINFO:
+			pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0]))
+			cm.Dst = pi.Addr[:]
+			cm.IfIndex = int(pi.Ifindex)
+		case syscall.IPV6_PATHMTU:
+			mi := (*syscall.IPv6MTUInfo)(unsafe.Pointer(&m.Data[0]))
+			cm.Dst = mi.Addr.Addr[:]
+			cm.IfIndex = int(mi.Addr.Scope_id)
+			cm.MTU = int(mi.Mtu)
+		}
+	}
+	return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	if cm == nil {
+		return
+	}
+	l, off := 0, 0
+	if cm.TrafficClass > 0 {
+		l += syscall.CmsgSpace(4)
+	}
+	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(syscall.SizeofInet6Pktinfo)
+	}
+	if len(cm.NextHop) == net.IPv6len {
+		l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if cm.TrafficClass > 0 {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_TCLASS
+			m.SetLen(syscall.CmsgLen(4))
+			data := oob[off+syscall.CmsgLen(0):]
+			*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass)
+			off += syscall.CmsgSpace(4)
+		}
+		if cm.HopLimit > 0 {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_HOPLIMIT
+			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 = syscall.IPV6_PKTINFO
+			m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
+			pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
+			if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+				copy(pi.Addr[:], ip)
+			}
+			if cm.IfIndex != 0 {
+				pi.Ifindex = uint32(cm.IfIndex)
+			}
+			off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+		}
+		if len(cm.NextHop) == net.IPv6len {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_NEXTHOP
+			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)
+			sa.Scope_id = uint32(cm.IfIndex)
+			off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+		}
+	}
+	return
+}

+ 217 - 0
ipv6/control_rfc3542_linux.go

@@ -0,0 +1,217 @@
+// 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"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+const (
+	// See /usr/include/linux/in6.h.
+	syscall_IPV6_RECVPATHMTU = syscall.IPV6_DSTOPTS + 1 + iota
+	syscall_IPV6_PATHMTU
+	syscall_IPV6_DONTFRAG
+)
+
+const pktinfo = FlagDst | FlagInterface
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+	opt.Lock()
+	defer opt.Unlock()
+	if cf&FlagTrafficClass != 0 {
+		if err := setIPv6ReceiveTrafficClass(fd, on); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagTrafficClass)
+		} else {
+			opt.clear(FlagTrafficClass)
+		}
+	}
+	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)
+		}
+	}
+	if cf&FlagPathMTU != 0 {
+		if err := setIPv6ReceivePathMTU(fd, on); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagPathMTU)
+		} else {
+			opt.clear(FlagPathMTU)
+		}
+	}
+	return nil
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	opt.Lock()
+	defer opt.Unlock()
+	l, off := 0, 0
+	if opt.isset(FlagTrafficClass) {
+		l += syscall.CmsgSpace(4)
+	}
+	if opt.isset(FlagHopLimit) {
+		l += syscall.CmsgSpace(4)
+	}
+	if opt.isset(pktinfo) {
+		l += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+	}
+	if opt.isset(FlagPathMTU) {
+		l += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if opt.isset(FlagTrafficClass) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_RECVTCLASS
+			m.SetLen(syscall.CmsgLen(4))
+			off += syscall.CmsgSpace(4)
+		}
+		if opt.isset(FlagHopLimit) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_RECVHOPLIMIT
+			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 = syscall.IPV6_RECVPKTINFO
+			m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
+			off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+		}
+		if opt.isset(FlagPathMTU) {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall_IPV6_RECVPATHMTU
+			m.SetLen(syscall.CmsgLen(syscall.SizeofIPv6MTUInfo))
+			off += syscall.CmsgSpace(syscall.SizeofIPv6MTUInfo)
+		}
+	}
+	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 syscall.IPV6_TCLASS:
+			cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
+		case syscall.IPV6_HOPLIMIT:
+			cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
+		case syscall.IPV6_PKTINFO:
+			pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&m.Data[0]))
+			cm.Dst = pi.Addr[:]
+			cm.IfIndex = int(pi.Ifindex)
+		case syscall_IPV6_PATHMTU:
+			mi := (*syscall.IPv6MTUInfo)(unsafe.Pointer(&m.Data[0]))
+			cm.Dst = mi.Addr.Addr[:]
+			cm.IfIndex = int(mi.Addr.Scope_id)
+			cm.MTU = int(mi.Mtu)
+		}
+	}
+	return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	if cm == nil {
+		return
+	}
+	l, off := 0, 0
+	if cm.TrafficClass > 0 {
+		l += syscall.CmsgSpace(4)
+	}
+	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(syscall.SizeofInet6Pktinfo)
+	}
+	if len(cm.NextHop) == net.IPv6len {
+		l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		if cm.TrafficClass > 0 {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_TCLASS
+			m.SetLen(syscall.CmsgLen(4))
+			data := oob[off+syscall.CmsgLen(0):]
+			*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass)
+			off += syscall.CmsgSpace(4)
+		}
+		if cm.HopLimit > 0 {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_HOPLIMIT
+			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 = syscall.IPV6_PKTINFO
+			m.SetLen(syscall.CmsgLen(syscall.SizeofInet6Pktinfo))
+			pi := (*syscall.Inet6Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
+			if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+				copy(pi.Addr[:], ip)
+			}
+			if cm.IfIndex != 0 {
+				pi.Ifindex = uint32(cm.IfIndex)
+			}
+			off += syscall.CmsgSpace(syscall.SizeofInet6Pktinfo)
+		}
+		if len(cm.NextHop) == net.IPv6len {
+			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
+			m.Level = ianaProtocolIPv6
+			m.Type = syscall.IPV6_NEXTHOP
+			m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
+			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
+			sa.Family = syscall.AF_INET6
+			copy(sa.Addr[:], cm.NextHop)
+			sa.Scope_id = uint32(cm.IfIndex)
+			off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+		}
+	}
+	return
+}

+ 27 - 0
ipv6/control_rfc3542_plan9.go

@@ -0,0 +1,27 @@
+// 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 "syscall"
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	// TODO(mikio): Implement this
+	return nil
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+	// TODO(mikio): Implement this
+	return nil, syscall.EPLAN9
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	// TODO(mikio): Implement this
+	return nil
+}

+ 27 - 0
ipv6/control_rfc3542_windows.go

@@ -0,0 +1,27 @@
+// 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 "syscall"
+
+func setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	// TODO(mikio): Implement this
+	return nil
+}
+
+func parseControlMessage(b []byte) (*ControlMessage, error) {
+	// TODO(mikio): Implement this
+	return nil, syscall.EWINDOWS
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	// TODO(mikio): Implement this
+	return nil
+}

+ 32 - 0
ipv6/control_test.go

@@ -0,0 +1,32 @@
+// 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 (
+	"sync"
+	"testing"
+)
+
+func TestControlFlags(t *testing.T) {
+	tf := FlagInterface | FlagPathMTU
+	opt := rawOpt{cflags: tf | FlagHopLimit}
+
+	type ffn func(ControlFlags)
+	var wg sync.WaitGroup
+	for _, fn := range []ffn{opt.set, opt.clear, opt.clear} {
+		wg.Add(1)
+		go func(fn ffn) {
+			defer wg.Done()
+			opt.Lock()
+			defer opt.Unlock()
+			fn(tf)
+		}(fn)
+	}
+	wg.Wait()
+
+	if opt.isset(tf) {
+		t.Fatalf("got %#x; expected %#x", opt.cflags, FlagHopLimit)
+	}
+}

+ 96 - 0
ipv6/dgramopt_plan9.go

@@ -0,0 +1,96 @@
+// 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"
+)
+
+// MulticastHopLimit returns the hop limit field value for outgoing
+// multicast packets.
+func (c *dgramOpt) MulticastHopLimit() (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}
+
+// SetMulticastHopLimit sets the hop limit field value for future
+// outgoing multicast packets.
+func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// MulticastInterface returns the default interface for multicast
+// packet transmissions.
+func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
+	// TODO(mikio): Implement this
+	return nil, syscall.EPLAN9
+}
+
+// SetMulticastInterface sets the default interface for future
+// multicast packet transmissions.
+func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// MulticastLoopback reports whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) MulticastLoopback() (bool, error) {
+	// TODO(mikio): Implement this
+	return false, syscall.EPLAN9
+}
+
+// SetMulticastLoopback sets whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) SetMulticastLoopback(on bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// JoinGroup joins the group address group on the interface ifi.
+// It uses the system assigned multicast interface when ifi is nil,
+// although this is not recommended because the assignment depends on
+// platforms and sometimes it might require routing configuration.
+func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// LeaveGroup leaves the group address group on the interface ifi.
+func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// Checksum reports whether the kernel will compute, store or verify a
+// checksum for both incoming and outgoing packets.  If on is true, it
+// returns an offset in bytes into the data of where the checksum
+// field is located.
+func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
+	// TODO(mikio): Implement this
+	return false, 0, syscall.EPLAN9
+}
+
+// SetChecksum enables the kernel checksum processing.  If on is ture,
+// the offset should be an offset in bytes into the data of where the
+// checksum field is located.
+func (c *dgramOpt) SetChecksum(on bool, offset int) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// ICMPFilter returns an ICMP filter.
+func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
+	// TODO(mikio): Implement this
+	return nil, syscall.EPLAN9
+}
+
+// SetICMPFilter deploys the ICMP filter.
+func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}

+ 178 - 0
ipv6/dgramopt_posix.go

@@ -0,0 +1,178 @@
+// 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 windows
+
+package ipv6
+
+import (
+	"net"
+	"syscall"
+)
+
+// MulticastHopLimit returns the hop limit field value for outgoing
+// multicast packets.
+func (c *dgramOpt) MulticastHopLimit() (int, error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return 0, err
+	}
+	return ipv6MulticastHopLimit(fd)
+}
+
+// SetMulticastHopLimit sets the hop limit field value for future
+// outgoing multicast packets.
+func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6MulticastHopLimit(fd, hoplim)
+}
+
+// MulticastInterface returns the default interface for multicast
+// packet transmissions.
+func (c *dgramOpt) MulticastInterface() (*net.Interface, error) {
+	if !c.ok() {
+		return nil, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return nil, err
+	}
+	return ipv6MulticastInterface(fd)
+}
+
+// SetMulticastInterface sets the default interface for future
+// multicast packet transmissions.
+func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6MulticastInterface(fd, ifi)
+}
+
+// MulticastLoopback reports whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) MulticastLoopback() (bool, error) {
+	if !c.ok() {
+		return false, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return false, err
+	}
+	return ipv6MulticastLoopback(fd)
+}
+
+// SetMulticastLoopback sets whether transmitted multicast packets
+// should be copied and send back to the originator.
+func (c *dgramOpt) SetMulticastLoopback(on bool) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6MulticastLoopback(fd, on)
+}
+
+// JoinGroup joins the group address group on the interface ifi.
+// It uses the system assigned multicast interface when ifi is nil,
+// although this is not recommended because the assignment depends on
+// platforms and sometimes it might require routing configuration.
+func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	grp := netAddrToIP16(group)
+	if grp == nil {
+		return errMissingAddress
+	}
+	return joinIPv6Group(fd, ifi, grp)
+}
+
+// LeaveGroup leaves the group address group on the interface ifi.
+func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	grp := netAddrToIP16(group)
+	if grp == nil {
+		return errMissingAddress
+	}
+	return leaveIPv6Group(fd, ifi, grp)
+}
+
+// Checksum reports whether the kernel will compute, store or verify a
+// checksum for both incoming and outgoing packets.  If on is true, it
+// returns an offset in bytes into the data of where the checksum
+// field is located.
+func (c *dgramOpt) Checksum() (on bool, offset int, err error) {
+	if !c.ok() {
+		return false, 0, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return false, 0, err
+	}
+	return ipv6Checksum(fd)
+}
+
+// SetChecksum enables the kernel checksum processing.  If on is ture,
+// the offset should be an offset in bytes into the data of where the
+// checksum field is located.
+func (c *dgramOpt) SetChecksum(on bool, offset int) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6Checksum(fd, on, offset)
+}
+
+// ICMPFilter returns an ICMP filter.
+func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) {
+	if !c.ok() {
+		return nil, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return nil, err
+	}
+	return ipv6ICMPFilter(fd)
+}
+
+// SetICMPFilter deploys the ICMP filter.
+func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6ICMPFilter(fd, f)
+}

+ 193 - 0
ipv6/doc.go

@@ -0,0 +1,193 @@
+// 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 implements IP-level socket options for the Internet
+// Protocol version 6.
+//
+// The package provides IP-level socket options that allow
+// manipulation of IPv6 facilities.  The IPv6 and socket options for
+// IPv6 are defined in RFC 2460, RFC 3493 and RFC 3542.
+//
+//
+// Unicasting
+//
+// The options for unicasting are available for net.TCPConn,
+// net.UDPConn and net.IPConn which are created as network connections
+// that use the IPv6 transport.  When a single TCP connection carrying
+// a data flow of multiple packets needs to indicate the flow is
+// important, ipv6.Conn is used to set the traffic class field on the
+// IPv6 header for each packet.
+//
+//	ln, err := net.Listen("tcp6", "[::]:1024")
+//	if err != nil {
+//		// error handling
+//	}
+//	defer ln.Close()
+//	for {
+//		c, err := ln.Accept()
+//		if err != nil {
+//			// error handling
+//		}
+//		go func(c net.Conn) {
+//			defer c.Close()
+//
+// The outgoing packets will be labeled DiffServ assured forwarding
+// class 1 low drop precedence, as known as AF11 packets.
+//
+//			if err := ipv6.NewConn(c).SetTrafficClass(DiffServAF11); err != nil {
+//				// error handling
+//			}
+//			if _, err := c.Write(data); err != nil {
+//				// error handling
+//			}
+//		}(c)
+//	}
+//
+//
+// Multicasting
+//
+// The options for multicasting are available for net.UDPConn and
+// net.IPconn which are created as network connections that use the
+// IPv6 transport.  A few network facilities must be prepared before
+// you begin multicasting, at a minimum joining network interfaces and
+// group addresses.
+//
+//	en0, err := net.InterfaceByName("en0")
+//	if err != nil {
+//		// error handling
+//	}
+//	en1, err := net.InterfaceByIndex(911)
+//	if err != nil {
+//		// error handling
+//	}
+//	group := net.ParseIP("ff02::114")
+//
+// First, an application listens to an appropriate address with an
+// appropriate service port.
+//
+//	c, err := net.ListenPacket("udp6", "[::]:1024")
+//	if err != nil {
+//		// error handling
+//	}
+//	defer c.Close()
+//
+// Second, the application joins groups, starts listening to the
+// group addresses on the specified network interfaces.  Note that
+// the service port for transport layer protocol does not matter with
+// this operation as joining groups affects only network and link
+// layer protocols, such as IPv6 and Ethernet.
+//
+//	p := ipv6.NewPacketConn(c)
+//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil {
+//		// error handling
+//	}
+//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil {
+//		// error handling
+//	}
+//
+// The application might set per packet control message transmissions
+// between the protocol stack within the kernel.  When the application
+// needs a destination address on an incoming packet,
+// SetControlMessage of ipv6.PacketConn is used to enable control
+// message transmissons.
+//
+//	if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil {
+//		// error handling
+//	}
+//
+// The application could identify whether the received packets are
+// of interest by using the control message that contains the
+// destination address of the received packet.
+//
+//	b := make([]byte, 1500)
+//	for {
+//		n, rcm, src, err := p.ReadFrom(b)
+//		if err != nil {
+//			// error handling
+//		}
+//		if rcm.Dst.IsMulticast() {
+//			if rcm.Dst.Equal(group)
+//				// joined group, do something
+//			} else {
+//				// unknown group, discard
+//				continue
+//			}
+//		}
+//
+// The application can also send both unicast and multicast packets.
+//
+//		p.SetTrafficClass(DiffServCS0)
+//		p.SetHopLimit(16)
+//		if _, err := p.WriteTo(data[:n], nil, src); err != nil {
+//			// error handling
+//		}
+//		dst := &net.UDPAddr{IP: group, Port: 1024}
+//		wcm := ipv6.ControlMessage{TrafficClass: DiffServCS7, HopLimit: 1}
+//		for _, ifi := range []*net.Interface{en0, en1} {
+//			wcm.IfIndex = ifi.Index
+//			if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil {
+//				// error handling
+//			}
+//		}
+//	}
+//
+//
+// More multicasting
+//
+// An application that uses PacketConn or RawConn might join the
+// multiple group addresses.  For example, a UDP listener with port
+// 1024 might join two different groups across over two different
+// network interfaces by using:
+//
+//	c, err := net.ListenPacket("udp6", "[::]:1024")
+//	if err != nil {
+//		// error handling
+//	}
+//	defer c.Close()
+//	p := ipv6.NewPacketConn(c)
+//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil {
+//		// error handling
+//	}
+//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
+//		// error handling
+//	}
+//	if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil {
+//		// error handling
+//	}
+//
+// It is possible for multiple UDP listeners that listen on the same
+// UDP port to join the same group address.  The net package will
+// provide a socket that listens to a wildcard address with reusable
+// UDP port when an appropriate multicast address prefix is passed to
+// the net.ListenPacket or net.ListenUDP.
+//
+//	c1, err := net.ListenPacket("udp6", "[ff02::]:1024")
+//	if err != nil {
+//		// error handling
+//	}
+//	defer c1.Close()
+//	c2, err := net.ListenPacket("udp6", "[ff02::]:1024")
+//	if err != nil {
+//		// error handling
+//	}
+//	defer c2.Close()
+//	p1 := ipv6.NewPacketConn(c1)
+//	if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
+//		// error handling
+//	}
+//	p2 := ipv6.NewPacketConn(c2)
+//	if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
+//		// error handling
+//	}
+//
+// Also it is possible for the application to leave or rejoin a
+// multicast group on the network interface.
+//
+//	if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil {
+//		// error handling
+//	}
+//	if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil {
+//		// error handling
+//	}
+package ipv6

+ 83 - 0
ipv6/endpoint.go

@@ -0,0 +1,83 @@
+// 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"
+)
+
+// A Conn represents a network endpoint that uses IPv6 transport.
+// It allows to set basic IP-level socket options such as traffic
+// class and hop limit.
+type Conn struct {
+	genericOpt
+}
+
+type genericOpt struct {
+	net.Conn
+}
+
+func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil }
+
+// PathMTU returns a path MTU value for the destination associated
+// with the endpoint.
+func (c *Conn) PathMTU() (int, error) {
+	if !c.genericOpt.ok() {
+		return 0, syscall.EINVAL
+	}
+	fd, err := c.genericOpt.sysfd()
+	if err != nil {
+		return 0, err
+	}
+	return ipv6PathMTU(fd)
+}
+
+// NewConn returns a new Conn.
+func NewConn(c net.Conn) *Conn {
+	return &Conn{
+		genericOpt: genericOpt{Conn: c},
+	}
+}
+
+// A PacketConn represents a packet network endpoint that uses IPv6
+// transport.  It is used to control several IP-level socket options
+// including IPv6 header manipulation.  It also provides datagram
+// based network I/O methods specific to the IPv6 and higher layer
+// protocols such as OSPF, GRE, and UDP.
+type PacketConn struct {
+	genericOpt
+	dgramOpt
+	payloadHandler
+}
+
+type dgramOpt struct {
+	net.PacketConn
+}
+
+func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil }
+
+// SetControlMessage allows to receive the per packet basis IP-level
+// socket options.
+func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error {
+	if !c.payloadHandler.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.payloadHandler.sysfd()
+	if err != nil {
+		return err
+	}
+	return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on)
+}
+
+// NewPacketConn returns a new PacketConn using c as its underlying
+// transport.
+func NewPacketConn(c net.PacketConn) *PacketConn {
+	return &PacketConn{
+		genericOpt:     genericOpt{Conn: c.(net.Conn)},
+		dgramOpt:       dgramOpt{PacketConn: c},
+		payloadHandler: payloadHandler{PacketConn: c},
+	}
+}

+ 237 - 0
ipv6/gen.go

@@ -0,0 +1,237 @@
+// 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 ignore
+
+// This program generates internet protocol constatns and tables by
+// reading IANA protocol registries.
+//
+// Usage:
+//	go run gen.go > iana.go
+package main
+
+import (
+	"bytes"
+	"encoding/xml"
+	"fmt"
+	"go/format"
+	"io"
+	"net/http"
+	"os"
+	"strconv"
+	"strings"
+)
+
+var registries = []struct {
+	url   string
+	parse func(io.Writer, io.Reader) error
+}{
+	{
+		"http://www.iana.org/assignments/icmpv6-parameters",
+		parseICMPv6Parameters,
+	},
+	{
+		"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
+		parseProtocolNumbers,
+	},
+}
+
+func main() {
+	var bb bytes.Buffer
+	fmt.Fprintf(&bb, "// go run gen.go\n")
+	fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
+	fmt.Fprintf(&bb, "package ipv6\n\n")
+	for _, r := range registries {
+		resp, err := http.Get(r.url)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		defer resp.Body.Close()
+		if resp.StatusCode != http.StatusOK {
+			fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
+			os.Exit(1)
+		}
+		if err := r.parse(&bb, resp.Body); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		fmt.Fprintf(&bb, "\n")
+	}
+	b, err := format.Source(bb.Bytes())
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+	os.Stdout.Write(b)
+}
+
+func parseICMPv6Parameters(w io.Writer, r io.Reader) error {
+	dec := xml.NewDecoder(r)
+	var icp icmpv6Parameters
+	if err := dec.Decode(&icp); err != nil {
+		return err
+	}
+	prs := icp.escape(1)
+	fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
+	fmt.Fprintf(w, "const (\n")
+	for _, pr := range prs {
+		if pr.Name == "" {
+			continue
+		}
+		fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value)
+		fmt.Fprintf(w, "// %s\n", pr.OrigName)
+	}
+	fmt.Fprintf(w, ")\n\n")
+	fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated)
+	fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n")
+	for _, pr := range prs {
+		if pr.Name == "" {
+			continue
+		}
+		fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName))
+	}
+	fmt.Fprintf(w, "}\n")
+	return nil
+}
+
+type icmpv6Parameters struct {
+	XMLName    xml.Name              `xml:"registry"`
+	Title      string                `xml:"title"`
+	Updated    string                `xml:"updated"`
+	Registries []icmpv6ParamRegistry `xml:"registry"`
+}
+
+type icmpv6ParamRegistry struct {
+	Title   string              `xml:"title"`
+	Records []icmpv6ParamRecord `xml:"record"`
+}
+
+type icmpv6ParamRecord struct {
+	Value string `xml:"value"`
+	Name  string `xml:"name"`
+}
+
+type canonICMPv6ParamRecord struct {
+	OrigName string
+	Name     string
+	Value    int
+}
+
+func (icp *icmpv6Parameters) escape(id int) []canonICMPv6ParamRecord {
+	prs := make([]canonICMPv6ParamRecord, len(icp.Registries[id].Records))
+	sr := strings.NewReplacer(
+		"Messages", "",
+		"Message", "",
+		"ICMP", "",
+		"+", "P",
+		"-", "",
+		"/", "",
+		".", "",
+		" ", "",
+	)
+	for i, pr := range icp.Registries[id].Records {
+		if strings.Contains(pr.Name, "Reserved") ||
+			strings.Contains(pr.Name, "Unassigned") ||
+			strings.Contains(pr.Name, "Deprecated") ||
+			strings.Contains(pr.Name, "Experiment") ||
+			strings.Contains(pr.Name, "experiment") {
+			continue
+		}
+		ss := strings.Split(pr.Name, "\n")
+		if len(ss) > 1 {
+			prs[i].Name = strings.Join(ss, " ")
+		} else {
+			prs[i].Name = ss[0]
+		}
+		s := strings.TrimSpace(prs[i].Name)
+		prs[i].OrigName = s
+		prs[i].Name = sr.Replace(s)
+		prs[i].Value, _ = strconv.Atoi(pr.Value)
+	}
+	return prs
+}
+
+func parseProtocolNumbers(w io.Writer, r io.Reader) error {
+	dec := xml.NewDecoder(r)
+	var pn protocolNumbers
+	if err := dec.Decode(&pn); err != nil {
+		return err
+	}
+	prs := pn.escape()
+	fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
+	fmt.Fprintf(w, "const (\n")
+	for _, pr := range prs {
+		if pr.Name == "" {
+			continue
+		}
+		fmt.Fprintf(w, "ianaProtocol%s = %d", pr.Name, pr.Value)
+		s := pr.Descr
+		if s == "" {
+			s = pr.OrigName
+		}
+		fmt.Fprintf(w, "// %s\n", s)
+	}
+	fmt.Fprintf(w, ")\n")
+	return nil
+}
+
+type protocolNumbers struct {
+	XMLName  xml.Name         `xml:"registry"`
+	Title    string           `xml:"title"`
+	Updated  string           `xml:"updated"`
+	RegTitle string           `xml:"registry>title"`
+	Note     string           `xml:"registry>note"`
+	Records  []protocolRecord `xml:"registry>record"`
+}
+
+type protocolRecord struct {
+	Value string `xml:"value"`
+	Name  string `xml:"name"`
+	Descr string `xml:"description"`
+}
+
+type canonProtocolRecord struct {
+	OrigName string
+	Name     string
+	Descr    string
+	Value    int
+}
+
+func (pn *protocolNumbers) escape() []canonProtocolRecord {
+	prs := make([]canonProtocolRecord, len(pn.Records))
+	sr := strings.NewReplacer(
+		"-in-", "in",
+		"-within-", "within",
+		"-over-", "over",
+		"+", "P",
+		"-", "",
+		"/", "",
+		".", "",
+		" ", "",
+	)
+	for i, pr := range pn.Records {
+		prs[i].OrigName = pr.Name
+		s := strings.TrimSpace(pr.Name)
+		switch pr.Name {
+		case "ISIS over IPv4":
+			prs[i].Name = "ISIS"
+		case "manet":
+			prs[i].Name = "MANET"
+		default:
+			prs[i].Name = sr.Replace(s)
+		}
+		ss := strings.Split(pr.Descr, "\n")
+		for i := range ss {
+			ss[i] = strings.TrimSpace(ss[i])
+		}
+		if len(ss) > 1 {
+			prs[i].Descr = strings.Join(ss, " ")
+		} else {
+			prs[i].Descr = ss[0]
+		}
+		prs[i].Value, _ = strconv.Atoi(pr.Value)
+	}
+	return prs
+}

+ 34 - 0
ipv6/genericopt_plan9.go

@@ -0,0 +1,34 @@
+// 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 "syscall"
+
+// TrafficClass returns the traffic class field value for outgoing
+// packets.
+func (c *genericOpt) TrafficClass() (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}
+
+// SetTrafficClass sets the traffic class field value for future
+// outgoing packets.
+func (c *genericOpt) SetTrafficClass(tclass int) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}
+
+// HopLimit returns the hop limit field value for outgoing packets.
+func (c *genericOpt) HopLimit() (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}
+
+// SetHopLimit sets the hop limit field value for future outgoing
+// packets.
+func (c *genericOpt) SetHopLimit(hoplim int) error {
+	// TODO(mikio): Implement this
+	return syscall.EPLAN9
+}

+ 60 - 0
ipv6/genericopt_posix.go

@@ -0,0 +1,60 @@
+// 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 windows
+
+package ipv6
+
+import "syscall"
+
+// TrafficClass returns the traffic class field value for outgoing
+// packets.
+func (c *genericOpt) TrafficClass() (int, error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return 0, err
+	}
+	return ipv6TrafficClass(fd)
+}
+
+// SetTrafficClass sets the traffic class field value for future
+// outgoing packets.
+func (c *genericOpt) SetTrafficClass(tclass int) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6TrafficClass(fd, tclass)
+}
+
+// HopLimit returns the hop limit field value for outgoing packets.
+func (c *genericOpt) HopLimit() (int, error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return 0, err
+	}
+	return ipv6HopLimit(fd)
+}
+
+// SetHopLimit sets the hop limit field value for future outgoing
+// packets.
+func (c *genericOpt) SetHopLimit(hoplim int) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	return setIPv6HopLimit(fd, hoplim)
+}

+ 196 - 0
ipv6/gentest.go

@@ -0,0 +1,196 @@
+// 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 ignore
+
+// This program generates internet protocol constants by reading IANA
+// protocol registries.
+//
+// Usage:
+//	go run gentest.go > iana_test.go
+package main
+
+import (
+	"bytes"
+	"encoding/xml"
+	"fmt"
+	"go/format"
+	"io"
+	"net/http"
+	"os"
+	"strconv"
+	"strings"
+)
+
+var registries = []struct {
+	url   string
+	parse func(io.Writer, io.Reader) error
+}{
+	{
+		"http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
+		parseDSCPRegistry,
+	},
+	{
+		"http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
+		parseTOSTCByte,
+	},
+}
+
+func main() {
+	var bb bytes.Buffer
+	fmt.Fprintf(&bb, "// go run gentv.go\n")
+	fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
+	fmt.Fprintf(&bb, "package ipv6_test\n\n")
+	for _, r := range registries {
+		resp, err := http.Get(r.url)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		defer resp.Body.Close()
+		if resp.StatusCode != http.StatusOK {
+			fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
+			os.Exit(1)
+		}
+		if err := r.parse(&bb, resp.Body); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		fmt.Fprintf(&bb, "\n")
+	}
+	b, err := format.Source(bb.Bytes())
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+	os.Stdout.Write(b)
+}
+
+func parseDSCPRegistry(w io.Writer, r io.Reader) error {
+	dec := xml.NewDecoder(r)
+	var dr dscpRegistry
+	if err := dec.Decode(&dr); err != nil {
+		return err
+	}
+	drs := dr.escape()
+	fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
+	fmt.Fprintf(w, "const (\n")
+	for _, dr := range drs {
+		fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
+		fmt.Fprintf(w, "// %s\n", dr.OrigName)
+	}
+	fmt.Fprintf(w, ")\n")
+	return nil
+}
+
+type dscpRegistry struct {
+	XMLName     xml.Name     `xml:"registry"`
+	Title       string       `xml:"title"`
+	Updated     string       `xml:"updated"`
+	Note        string       `xml:"note"`
+	RegTitle    string       `xml:"registry>title"`
+	PoolRecords []dscpRecord `xml:"registry>record"`
+	Records     []dscpRecord `xml:"registry>registry>record"`
+}
+
+type dscpRecord struct {
+	Name  string `xml:"name"`
+	Space string `xml:"space"`
+}
+
+type canonDSCPRecord struct {
+	OrigName string
+	Name     string
+	Value    int
+}
+
+func (drr *dscpRegistry) escape() []canonDSCPRecord {
+	drs := make([]canonDSCPRecord, len(drr.Records))
+	sr := strings.NewReplacer(
+		"+", "",
+		"-", "",
+		"/", "",
+		".", "",
+		" ", "",
+	)
+	for i, dr := range drr.Records {
+		s := strings.TrimSpace(dr.Name)
+		drs[i].OrigName = s
+		drs[i].Name = sr.Replace(s)
+		n, err := strconv.ParseUint(dr.Space, 2, 8)
+		if err != nil {
+			continue
+		}
+		drs[i].Value = int(n) << 2
+	}
+	return drs
+}
+
+func parseTOSTCByte(w io.Writer, r io.Reader) error {
+	dec := xml.NewDecoder(r)
+	var ttb tosTCByte
+	if err := dec.Decode(&ttb); err != nil {
+		return err
+	}
+	trs := ttb.escape()
+	fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
+	fmt.Fprintf(w, "const (\n")
+	for _, tr := range trs {
+		fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
+		fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
+	}
+	fmt.Fprintf(w, ")\n")
+	return nil
+}
+
+type tosTCByte struct {
+	XMLName  xml.Name          `xml:"registry"`
+	Title    string            `xml:"title"`
+	Updated  string            `xml:"updated"`
+	Note     string            `xml:"note"`
+	RegTitle string            `xml:"registry>title"`
+	Records  []tosTCByteRecord `xml:"registry>record"`
+}
+
+type tosTCByteRecord struct {
+	Binary  string `xml:"binary"`
+	Keyword string `xml:"keyword"`
+}
+
+type canonTOSTCByteRecord struct {
+	OrigKeyword string
+	Keyword     string
+	Value       int
+}
+
+func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
+	trs := make([]canonTOSTCByteRecord, len(ttb.Records))
+	sr := strings.NewReplacer(
+		"Capable", "",
+		"(", "",
+		")", "",
+		"+", "",
+		"-", "",
+		"/", "",
+		".", "",
+		" ", "",
+	)
+	for i, tr := range ttb.Records {
+		s := strings.TrimSpace(tr.Keyword)
+		trs[i].OrigKeyword = s
+		ss := strings.Split(s, " ")
+		if len(ss) > 1 {
+			trs[i].Keyword = strings.Join(ss[1:], " ")
+		} else {
+			trs[i].Keyword = ss[0]
+		}
+		trs[i].Keyword = sr.Replace(trs[i].Keyword)
+		n, err := strconv.ParseUint(tr.Binary, 2, 8)
+		if err != nil {
+			continue
+		}
+		trs[i].Value = int(n)
+	}
+	return trs
+}

+ 28 - 0
ipv6/helper.go

@@ -0,0 +1,28 @@
+// 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"
+
+func boolint(b bool) int {
+	if b {
+		return 1
+	}
+	return 0
+}
+
+func netAddrToIP16(a net.Addr) net.IP {
+	switch v := a.(type) {
+	case *net.UDPAddr:
+		if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
+			return ip
+		}
+	case *net.IPAddr:
+		if ip := v.IP.To16(); ip != nil && ip.To4() == nil {
+			return ip
+		}
+	}
+	return nil
+}

+ 22 - 0
ipv6/helper_plan9.go

@@ -0,0 +1,22 @@
+// 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 "syscall"
+
+func (c *genericOpt) sysfd() (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}
+
+func (c *dgramOpt) sysfd() (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}
+
+func (c *payloadHandler) sysfd() (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}

+ 46 - 0
ipv6/helper_unix.go

@@ -0,0 +1,46 @@
+// 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 (
+	"net"
+	"reflect"
+)
+
+func (c *genericOpt) sysfd() (int, error) {
+	switch p := c.Conn.(type) {
+	case *net.TCPConn, *net.UDPConn, *net.IPConn:
+		return sysfd(p)
+	}
+	return 0, errInvalidConnType
+}
+
+func (c *dgramOpt) sysfd() (int, error) {
+	switch p := c.PacketConn.(type) {
+	case *net.UDPConn, *net.IPConn:
+		return sysfd(p.(net.Conn))
+	}
+	return 0, errInvalidConnType
+}
+
+func (c *payloadHandler) sysfd() (int, error) {
+	return sysfd(c.PacketConn.(net.Conn))
+}
+
+func sysfd(c net.Conn) (int, error) {
+	cv := reflect.ValueOf(c)
+	switch ce := cv.Elem(); ce.Kind() {
+	case reflect.Struct:
+		nfd := ce.FieldByName("conn").FieldByName("fd")
+		switch fe := nfd.Elem(); fe.Kind() {
+		case reflect.Struct:
+			fd := fe.FieldByName("sysfd")
+			return int(fd.Int()), nil
+		}
+	}
+	return 0, errInvalidConnType
+}

+ 45 - 0
ipv6/helper_windows.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"
+	"reflect"
+	"syscall"
+)
+
+func (c *genericOpt) sysfd() (syscall.Handle, error) {
+	switch p := c.Conn.(type) {
+	case *net.TCPConn, *net.UDPConn, *net.IPConn:
+		return sysfd(p)
+	}
+	return syscall.InvalidHandle, errInvalidConnType
+}
+
+func (c *dgramOpt) sysfd() (syscall.Handle, error) {
+	switch p := c.PacketConn.(type) {
+	case *net.UDPConn, *net.IPConn:
+		return sysfd(p.(net.Conn))
+	}
+	return syscall.InvalidHandle, errInvalidConnType
+}
+
+func (c *payloadHandler) sysfd() (syscall.Handle, error) {
+	return sysfd(c.PacketConn.(net.Conn))
+}
+
+func sysfd(c net.Conn) (syscall.Handle, error) {
+	cv := reflect.ValueOf(c)
+	switch ce := cv.Elem(); ce.Kind() {
+	case reflect.Struct:
+		netfd := ce.FieldByName("conn").FieldByName("fd")
+		switch fe := netfd.Elem(); fe.Kind() {
+		case reflect.Struct:
+			fd := fe.FieldByName("sysfd")
+			return syscall.Handle(fd.Uint()), nil
+		}
+	}
+	return syscall.InvalidHandle, errInvalidConnType
+}

+ 224 - 0
ipv6/iana.go

@@ -0,0 +1,224 @@
+// go run gen.go
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package ipv6
+
+// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2012-11-12
+const (
+	ICMPTypeDestinationUnreachable                ICMPType = 1   // Destination Unreachable
+	ICMPTypePacketTooBig                          ICMPType = 2   // Packet Too Big
+	ICMPTypeTimeExceeded                          ICMPType = 3   // Time Exceeded
+	ICMPTypeParameterProblem                      ICMPType = 4   // Parameter Problem
+	ICMPTypeEchoRequest                           ICMPType = 128 // Echo Request
+	ICMPTypeEchoReply                             ICMPType = 129 // Echo Reply
+	ICMPTypeMulticastListenerQuery                ICMPType = 130 // Multicast Listener Query
+	ICMPTypeMulticastListenerReport               ICMPType = 131 // Multicast Listener Report
+	ICMPTypeMulticastListenerDone                 ICMPType = 132 // Multicast Listener Done
+	ICMPTypeRouterSolicitation                    ICMPType = 133 // Router Solicitation
+	ICMPTypeRouterAdvertisement                   ICMPType = 134 // Router Advertisement
+	ICMPTypeNeighborSolicitation                  ICMPType = 135 // Neighbor Solicitation
+	ICMPTypeNeighborAdvertisement                 ICMPType = 136 // Neighbor Advertisement
+	ICMPTypeRedirect                              ICMPType = 137 // Redirect Message
+	ICMPTypeRouterRenumbering                     ICMPType = 138 // Router Renumbering
+	ICMPTypeNodeInformationQuery                  ICMPType = 139 // ICMP Node Information Query
+	ICMPTypeNodeInformationResponse               ICMPType = 140 // ICMP Node Information Response
+	ICMPTypeInverseNeighborDiscoverySolicitation  ICMPType = 141 // Inverse Neighbor Discovery Solicitation Message
+	ICMPTypeInverseNeighborDiscoveryAdvertisement ICMPType = 142 // Inverse Neighbor Discovery Advertisement Message
+	ICMPTypeVersion2MulticastListenerReport       ICMPType = 143 // Version 2 Multicast Listener Report
+	ICMPTypeHomeAgentAddressDiscoveryRequest      ICMPType = 144 // Home Agent Address Discovery Request Message
+	ICMPTypeHomeAgentAddressDiscoveryReply        ICMPType = 145 // Home Agent Address Discovery Reply Message
+	ICMPTypeMobilePrefixSolicitation              ICMPType = 146 // Mobile Prefix Solicitation
+	ICMPTypeMobilePrefixAdvertisement             ICMPType = 147 // Mobile Prefix Advertisement
+	ICMPTypeCertificationPathSolicitation         ICMPType = 148 // Certification Path Solicitation Message
+	ICMPTypeCertificationPathAdvertisement        ICMPType = 149 // Certification Path Advertisement Message
+	ICMPTypeMulticastRouterAdvertisement          ICMPType = 151 // Multicast Router Advertisement
+	ICMPTypeMulticastRouterSolicitation           ICMPType = 152 // Multicast Router Solicitation
+	ICMPTypeMulticastRouterTermination            ICMPType = 153 // Multicast Router Termination
+	ICMPTypeFMIPv6                                ICMPType = 154 // FMIPv6 Messages
+	ICMPTypeRPLControl                            ICMPType = 155 // RPL Control Message
+	ICMPTypeILNPv6LocatorUpdate                   ICMPType = 156 // ILNPv6 Locator Update Message
+	ICMPTypeDuplicateAddressRequest               ICMPType = 157 // Duplicate Address Request
+	ICMPTypeDuplicateAddressConfirmation          ICMPType = 158 // Duplicate Address Confirmation
+)
+
+// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2012-11-12
+var icmpTypes = map[ICMPType]string{
+	1:   "destination unreachable",
+	2:   "packet too big",
+	3:   "time exceeded",
+	4:   "parameter problem",
+	128: "echo request",
+	129: "echo reply",
+	130: "multicast listener query",
+	131: "multicast listener report",
+	132: "multicast listener done",
+	133: "router solicitation",
+	134: "router advertisement",
+	135: "neighbor solicitation",
+	136: "neighbor advertisement",
+	137: "redirect message",
+	138: "router renumbering",
+	139: "icmp node information query",
+	140: "icmp node information response",
+	141: "inverse neighbor discovery solicitation message",
+	142: "inverse neighbor discovery advertisement message",
+	143: "version 2 multicast listener report",
+	144: "home agent address discovery request message",
+	145: "home agent address discovery reply message",
+	146: "mobile prefix solicitation",
+	147: "mobile prefix advertisement",
+	148: "certification path solicitation message",
+	149: "certification path advertisement message",
+	151: "multicast router advertisement",
+	152: "multicast router solicitation",
+	153: "multicast router termination",
+	154: "fmipv6 messages",
+	155: "rpl control message",
+	156: "ilnpv6 locator update message",
+	157: "duplicate address request",
+	158: "duplicate address confirmation",
+}
+
+// Protocol Numbers, Updated: 2013-02-17
+const (
+	ianaProtocolHOPOPT         = 0   // IPv6 Hop-by-Hop Option
+	ianaProtocolICMP           = 1   // Internet Control Message
+	ianaProtocolIGMP           = 2   // Internet Group Management
+	ianaProtocolGGP            = 3   // Gateway-to-Gateway
+	ianaProtocolIPv4           = 4   // IPv4 encapsulation
+	ianaProtocolST             = 5   // Stream
+	ianaProtocolTCP            = 6   // Transmission Control
+	ianaProtocolCBT            = 7   // CBT
+	ianaProtocolEGP            = 8   // Exterior Gateway Protocol
+	ianaProtocolIGP            = 9   // any private interior gateway (used by Cisco for their IGRP)
+	ianaProtocolBBNRCCMON      = 10  // BBN RCC Monitoring
+	ianaProtocolNVPII          = 11  // Network Voice Protocol
+	ianaProtocolPUP            = 12  // PUP
+	ianaProtocolARGUS          = 13  // ARGUS
+	ianaProtocolEMCON          = 14  // EMCON
+	ianaProtocolXNET           = 15  // Cross Net Debugger
+	ianaProtocolCHAOS          = 16  // Chaos
+	ianaProtocolUDP            = 17  // User Datagram
+	ianaProtocolMUX            = 18  // Multiplexing
+	ianaProtocolDCNMEAS        = 19  // DCN Measurement Subsystems
+	ianaProtocolHMP            = 20  // Host Monitoring
+	ianaProtocolPRM            = 21  // Packet Radio Measurement
+	ianaProtocolXNSIDP         = 22  // XEROX NS IDP
+	ianaProtocolTRUNK1         = 23  // Trunk-1
+	ianaProtocolTRUNK2         = 24  // Trunk-2
+	ianaProtocolLEAF1          = 25  // Leaf-1
+	ianaProtocolLEAF2          = 26  // Leaf-2
+	ianaProtocolRDP            = 27  // Reliable Data Protocol
+	ianaProtocolIRTP           = 28  // Internet Reliable Transaction
+	ianaProtocolISOTP4         = 29  // ISO Transport Protocol Class 4
+	ianaProtocolNETBLT         = 30  // Bulk Data Transfer Protocol
+	ianaProtocolMFENSP         = 31  // MFE Network Services Protocol
+	ianaProtocolMERITINP       = 32  // MERIT Internodal Protocol
+	ianaProtocolDCCP           = 33  // Datagram Congestion Control Protocol
+	ianaProtocol3PC            = 34  // Third Party Connect Protocol
+	ianaProtocolIDPR           = 35  // Inter-Domain Policy Routing Protocol
+	ianaProtocolXTP            = 36  // XTP
+	ianaProtocolDDP            = 37  // Datagram Delivery Protocol
+	ianaProtocolIDPRCMTP       = 38  // IDPR Control Message Transport Proto
+	ianaProtocolTPPP           = 39  // TP++ Transport Protocol
+	ianaProtocolIL             = 40  // IL Transport Protocol
+	ianaProtocolIPv6           = 41  // IPv6 encapsulation
+	ianaProtocolSDRP           = 42  // Source Demand Routing Protocol
+	ianaProtocolIPv6Route      = 43  // Routing Header for IPv6
+	ianaProtocolIPv6Frag       = 44  // Fragment Header for IPv6
+	ianaProtocolIDRP           = 45  // Inter-Domain Routing Protocol
+	ianaProtocolRSVP           = 46  // Reservation Protocol
+	ianaProtocolGRE            = 47  // Generic Routing Encapsulation
+	ianaProtocolDSR            = 48  // Dynamic Source Routing Protocol
+	ianaProtocolBNA            = 49  // BNA
+	ianaProtocolESP            = 50  // Encap Security Payload
+	ianaProtocolAH             = 51  // Authentication Header
+	ianaProtocolINLSP          = 52  // Integrated Net Layer Security  TUBA
+	ianaProtocolSWIPE          = 53  // IP with Encryption
+	ianaProtocolNARP           = 54  // NBMA Address Resolution Protocol
+	ianaProtocolMOBILE         = 55  // IP Mobility
+	ianaProtocolTLSP           = 56  // Transport Layer Security Protocol using Kryptonet key management
+	ianaProtocolSKIP           = 57  // SKIP
+	ianaProtocolIPv6ICMP       = 58  // ICMP for IPv6
+	ianaProtocolIPv6NoNxt      = 59  // No Next Header for IPv6
+	ianaProtocolIPv6Opts       = 60  // Destination Options for IPv6
+	ianaProtocolCFTP           = 62  // CFTP
+	ianaProtocolSATEXPAK       = 64  // SATNET and Backroom EXPAK
+	ianaProtocolKRYPTOLAN      = 65  // Kryptolan
+	ianaProtocolRVD            = 66  // MIT Remote Virtual Disk Protocol
+	ianaProtocolIPPC           = 67  // Internet Pluribus Packet Core
+	ianaProtocolSATMON         = 69  // SATNET Monitoring
+	ianaProtocolVISA           = 70  // VISA Protocol
+	ianaProtocolIPCV           = 71  // Internet Packet Core Utility
+	ianaProtocolCPNX           = 72  // Computer Protocol Network Executive
+	ianaProtocolCPHB           = 73  // Computer Protocol Heart Beat
+	ianaProtocolWSN            = 74  // Wang Span Network
+	ianaProtocolPVP            = 75  // Packet Video Protocol
+	ianaProtocolBRSATMON       = 76  // Backroom SATNET Monitoring
+	ianaProtocolSUNND          = 77  // SUN ND PROTOCOL-Temporary
+	ianaProtocolWBMON          = 78  // WIDEBAND Monitoring
+	ianaProtocolWBEXPAK        = 79  // WIDEBAND EXPAK
+	ianaProtocolISOIP          = 80  // ISO Internet Protocol
+	ianaProtocolVMTP           = 81  // VMTP
+	ianaProtocolSECUREVMTP     = 82  // SECURE-VMTP
+	ianaProtocolVINES          = 83  // VINES
+	ianaProtocolTTP            = 84  // TTP
+	ianaProtocolIPTM           = 84  // Protocol Internet Protocol Traffic Manager
+	ianaProtocolNSFNETIGP      = 85  // NSFNET-IGP
+	ianaProtocolDGP            = 86  // Dissimilar Gateway Protocol
+	ianaProtocolTCF            = 87  // TCF
+	ianaProtocolEIGRP          = 88  // EIGRP
+	ianaProtocolOSPFIGP        = 89  // OSPFIGP
+	ianaProtocolSpriteRPC      = 90  // Sprite RPC Protocol
+	ianaProtocolLARP           = 91  // Locus Address Resolution Protocol
+	ianaProtocolMTP            = 92  // Multicast Transport Protocol
+	ianaProtocolAX25           = 93  // AX.25 Frames
+	ianaProtocolIPIP           = 94  // IP-within-IP Encapsulation Protocol
+	ianaProtocolMICP           = 95  // Mobile Internetworking Control Pro.
+	ianaProtocolSCCSP          = 96  // Semaphore Communications Sec. Pro.
+	ianaProtocolETHERIP        = 97  // Ethernet-within-IP Encapsulation
+	ianaProtocolENCAP          = 98  // Encapsulation Header
+	ianaProtocolGMTP           = 100 // GMTP
+	ianaProtocolIFMP           = 101 // Ipsilon Flow Management Protocol
+	ianaProtocolPNNI           = 102 // PNNI over IP
+	ianaProtocolPIM            = 103 // Protocol Independent Multicast
+	ianaProtocolARIS           = 104 // ARIS
+	ianaProtocolSCPS           = 105 // SCPS
+	ianaProtocolQNX            = 106 // QNX
+	ianaProtocolAN             = 107 // Active Networks
+	ianaProtocolIPComp         = 108 // IP Payload Compression Protocol
+	ianaProtocolSNP            = 109 // Sitara Networks Protocol
+	ianaProtocolCompaqPeer     = 110 // Compaq Peer Protocol
+	ianaProtocolIPXinIP        = 111 // IPX in IP
+	ianaProtocolVRRP           = 112 // Virtual Router Redundancy Protocol
+	ianaProtocolPGM            = 113 // PGM Reliable Transport Protocol
+	ianaProtocolL2TP           = 115 // Layer Two Tunneling Protocol
+	ianaProtocolDDX            = 116 // D-II Data Exchange (DDX)
+	ianaProtocolIATP           = 117 // Interactive Agent Transfer Protocol
+	ianaProtocolSTP            = 118 // Schedule Transfer Protocol
+	ianaProtocolSRP            = 119 // SpectraLink Radio Protocol
+	ianaProtocolUTI            = 120 // UTI
+	ianaProtocolSMP            = 121 // Simple Message Protocol
+	ianaProtocolSM             = 122 // SM
+	ianaProtocolPTP            = 123 // Performance Transparency Protocol
+	ianaProtocolISIS           = 124 // ISIS over IPv4
+	ianaProtocolFIRE           = 125 // FIRE
+	ianaProtocolCRTP           = 126 // Combat Radio Transport Protocol
+	ianaProtocolCRUDP          = 127 // Combat Radio User Datagram
+	ianaProtocolSSCOPMCE       = 128 // SSCOPMCE
+	ianaProtocolIPLT           = 129 // IPLT
+	ianaProtocolSPS            = 130 // Secure Packet Shield
+	ianaProtocolPIPE           = 131 // Private IP Encapsulation within IP
+	ianaProtocolSCTP           = 132 // Stream Control Transmission Protocol
+	ianaProtocolFC             = 133 // Fibre Channel
+	ianaProtocolRSVPE2EIGNORE  = 134 // RSVP-E2E-IGNORE
+	ianaProtocolMobilityHeader = 135 // Mobility Header
+	ianaProtocolUDPLite        = 136 // UDPLite
+	ianaProtocolMPLSinIP       = 137 // MPLS-in-IP
+	ianaProtocolMANET          = 138 // MANET Protocols
+	ianaProtocolHIP            = 139 // Host Identity Protocol
+	ianaProtocolShim6          = 140 // Shim6 Protocol
+	ianaProtocolWESP           = 141 // Wrapped Encapsulating Security Payload
+	ianaProtocolROHC           = 142 // Robust Header Compression
+	ianaProtocolReserved       = 255 // Reserved
+)

+ 38 - 0
ipv6/iana_test.go

@@ -0,0 +1,38 @@
+// go run gentv.go
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package ipv6_test
+
+// Differentiated Services Field Codepoints, Updated: 2010-05-11
+const (
+	DiffServCS0        = 0x0  // CS0
+	DiffServCS1        = 0x20 // CS1
+	DiffServCS2        = 0x40 // CS2
+	DiffServCS3        = 0x60 // CS3
+	DiffServCS4        = 0x80 // CS4
+	DiffServCS5        = 0xa0 // CS5
+	DiffServCS6        = 0xc0 // CS6
+	DiffServCS7        = 0xe0 // CS7
+	DiffServAF11       = 0x28 // AF11
+	DiffServAF12       = 0x30 // AF12
+	DiffServAF13       = 0x38 // AF13
+	DiffServAF21       = 0x48 // AF21
+	DiffServAF22       = 0x50 // AF22
+	DiffServAF23       = 0x58 // AF23
+	DiffServAF31       = 0x68 // AF31
+	DiffServAF32       = 0x70 // AF32
+	DiffServAF33       = 0x78 // AF33
+	DiffServAF41       = 0x88 // AF41
+	DiffServAF42       = 0x90 // AF42
+	DiffServAF43       = 0x98 // AF43
+	DiffServEFPHB      = 0xb8 // EF PHB
+	DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
+)
+
+// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06
+const (
+	NotECNTransport       = 0x0 // Not-ECT (Not ECN-Capable Transport)
+	ECNTransport1         = 0x1 // ECT(1) (ECN-Capable Transport(1))
+	ECNTransport0         = 0x2 // ECT(0) (ECN-Capable Transport(0))
+	CongestionExperienced = 0x3 // CE (Congestion Experienced)
+)

+ 46 - 0
ipv6/icmp.go

@@ -0,0 +1,46 @@
+// 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 "sync"
+
+// An ICMPType represents a type of ICMP message.
+type ICMPType int
+
+func (typ ICMPType) String() string {
+	s, ok := icmpTypes[typ]
+	if !ok {
+		return "<nil>"
+	}
+	return s
+}
+
+// An ICMPFilter represents an ICMP message filter for incoming
+// packets.
+type ICMPFilter struct {
+	mu sync.RWMutex
+	rawICMPFilter
+}
+
+// Set sets the ICMP type and filter action to the filter.
+func (f *ICMPFilter) Set(typ ICMPType, block bool) {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+	f.set(typ, block)
+}
+
+// SetAll sets the filter action to the filter.
+func (f *ICMPFilter) SetAll(block bool) {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+	f.setAll(block)
+}
+
+// WillBlock reports whether the ICMP type will be blocked.
+func (f *ICMPFilter) WillBlock(typ ICMPType) bool {
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+	return f.willBlock(typ)
+}

+ 35 - 0
ipv6/icmp_bsd.go

@@ -0,0 +1,35 @@
+// 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 netbsd openbsd
+
+package ipv6
+
+import "syscall"
+
+type rawICMPFilter struct {
+	syscall.ICMPv6Filter
+}
+
+func (f *rawICMPFilter) set(typ ICMPType, block bool) {
+	if block {
+		f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31)
+	} else {
+		f.Filt[typ>>5] |= 1 << (uint32(typ) & 31)
+	}
+}
+
+func (f *rawICMPFilter) setAll(block bool) {
+	for i := 0; i < len(f.Filt); i++ {
+		if block {
+			f.Filt[i] = 0
+		} else {
+			f.Filt[i] = 1<<32 - 1
+		}
+	}
+}
+
+func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
+	return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0
+}

+ 33 - 0
ipv6/icmp_linux.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 "syscall"
+
+type rawICMPFilter struct {
+	syscall.ICMPv6Filter
+}
+
+func (f *rawICMPFilter) set(typ ICMPType, block bool) {
+	if block {
+		f.Data[typ>>5] |= 1 << (uint32(typ) & 31)
+	} else {
+		f.Data[typ>>5] &^= 1 << (uint32(typ) & 31)
+	}
+}
+
+func (f *rawICMPFilter) setAll(block bool) {
+	for i := 0; i < len(f.Data); i++ {
+		if block {
+			f.Data[i] = 1<<32 - 1
+		} else {
+			f.Data[i] = 0
+		}
+	}
+}
+
+func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
+	return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0
+}

+ 22 - 0
ipv6/icmp_plan9.go

@@ -0,0 +1,22 @@
+// 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 rawICMPFilter struct {
+	// TODO(mikio): Implement this
+}
+
+func (f *rawICMPFilter) set(typ ICMPType, block bool) {
+	// TODO(mikio): Implement this
+}
+
+func (f *rawICMPFilter) setAll(block bool) {
+	// TODO(mikio): Implement this
+}
+
+func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
+	// TODO(mikio): Implement this
+	return false
+}

+ 81 - 0
ipv6/icmp_test.go

@@ -0,0 +1,81 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"net"
+	"os"
+	"reflect"
+	"runtime"
+	"sync"
+	"testing"
+)
+
+func TestICMPFilter(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	var f ipv6.ICMPFilter
+	for _, toggle := range []bool{false, true} {
+		f.SetAll(toggle)
+		var wg sync.WaitGroup
+		for _, typ := range []ipv6.ICMPType{
+			ipv6.ICMPTypeDestinationUnreachable,
+			ipv6.ICMPTypeEchoReply,
+			ipv6.ICMPTypeNeighborSolicitation,
+			ipv6.ICMPTypeDuplicateAddressConfirmation,
+		} {
+			wg.Add(1)
+			go func(typ ipv6.ICMPType) {
+				defer wg.Done()
+				f.Set(typ, false)
+				if f.WillBlock(typ) {
+					t.Errorf("ipv6.ICMPFilter.Set(%v, false) failed", typ)
+				}
+				f.Set(typ, true)
+				if !f.WillBlock(typ) {
+					t.Errorf("ipv6.ICMPFilter.Set(%v, true) failed", typ)
+				}
+			}(typ)
+		}
+		wg.Wait()
+	}
+}
+
+func TestSetICMPFilter(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+
+	c, err := net.ListenPacket("ip6:ipv6-icmp", "::1")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	p := ipv6.NewPacketConn(c)
+
+	var f ipv6.ICMPFilter
+	f.SetAll(true)
+	f.Set(ipv6.ICMPTypeEchoRequest, false)
+	f.Set(ipv6.ICMPTypeEchoReply, false)
+	if err := p.SetICMPFilter(&f); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
+	}
+	kf, err := p.ICMPFilter()
+	if err != nil {
+		t.Fatalf("ipv6.PacketConn.ICMPFilter failed: %v", err)
+	}
+	if !reflect.DeepEqual(kf, &f) {
+		t.Fatalf("got unexpected filter %#v; expected %#v", kf, f)
+	}
+}

+ 22 - 0
ipv6/icmp_windows.go

@@ -0,0 +1,22 @@
+// 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 rawICMPFilter struct {
+	// TODO(mikio): Implement this
+}
+
+func (f *rawICMPFilter) set(typ ICMPType, block bool) {
+	// TODO(mikio): Implement this
+}
+
+func (f *rawICMPFilter) setAll(block bool) {
+	// TODO(mikio): Implement this
+}
+
+func (f *rawICMPFilter) willBlock(typ ICMPType) bool {
+	// TODO(mikio): Implement this
+	return false
+}

+ 112 - 0
ipv6/mockicmp_test.go

@@ -0,0 +1,112 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"errors"
+)
+
+// icmpMessage represents an ICMP message.
+type icmpMessage struct {
+	Type     ipv6.ICMPType   // type
+	Code     int             // code
+	Checksum int             // checksum
+	Body     icmpMessageBody // body
+}
+
+// icmpMessageBody represents an ICMP message body.
+type icmpMessageBody interface {
+	Len() int
+	Marshal() ([]byte, error)
+}
+
+// Marshal returns the binary enconding of the ICMP echo request or
+// reply message m.
+func (m *icmpMessage) Marshal() ([]byte, error) {
+	b := []byte{byte(m.Type), byte(m.Code), 0, 0}
+	if m.Body != nil && m.Body.Len() != 0 {
+		mb, err := m.Body.Marshal()
+		if err != nil {
+			return nil, err
+		}
+		b = append(b, mb...)
+	}
+	switch m.Type {
+	case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
+		return b, nil
+	}
+	csumcv := len(b) - 1 // checksum coverage
+	s := uint32(0)
+	for i := 0; i < csumcv; i += 2 {
+		s += uint32(b[i+1])<<8 | uint32(b[i])
+	}
+	if csumcv&1 == 0 {
+		s += uint32(b[csumcv])
+	}
+	s = s>>16 + s&0xffff
+	s = s + s>>16
+	// Place checksum back in header; using ^= avoids the
+	// assumption the checksum bytes are zero.
+	b[2] ^= byte(^s & 0xff)
+	b[3] ^= byte(^s >> 8)
+	return b, nil
+}
+
+// parseICMPMessage parses b as an ICMP message.
+func parseICMPMessage(b []byte) (*icmpMessage, error) {
+	msglen := len(b)
+	if msglen < 4 {
+		return nil, errors.New("message too short")
+	}
+	m := &icmpMessage{Type: ipv6.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
+	if msglen > 4 {
+		var err error
+		switch m.Type {
+		case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
+			m.Body, err = parseICMPEcho(b[4:])
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+	return m, nil
+}
+
+// imcpEcho represenets an ICMP echo request or reply message body.
+type icmpEcho struct {
+	ID   int    // identifier
+	Seq  int    // sequence number
+	Data []byte // data
+}
+
+func (p *icmpEcho) Len() int {
+	if p == nil {
+		return 0
+	}
+	return 4 + len(p.Data)
+}
+
+// Marshal returns the binary enconding of the ICMP echo request or
+// reply message body p.
+func (p *icmpEcho) Marshal() ([]byte, error) {
+	b := make([]byte, 4+len(p.Data))
+	b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff)
+	b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff)
+	copy(b[4:], p.Data)
+	return b, nil
+}
+
+// parseICMPEcho parses b as an ICMP echo request or reply message
+// body.
+func parseICMPEcho(b []byte) (*icmpEcho, error) {
+	bodylen := len(b)
+	p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
+	if bodylen > 4 {
+		p.Data = make([]byte, bodylen-4)
+		copy(p.Data, b[4:])
+	}
+	return p, nil
+}

+ 110 - 0
ipv6/mocktransponder_test.go

@@ -0,0 +1,110 @@
+// 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_test
+
+import (
+	"net"
+	"testing"
+)
+
+func isLinkLocalUnicast(ip net.IP) bool {
+	return ip.To4() == nil && ip.To16() != nil && ip.IsLinkLocalUnicast()
+}
+
+func loopbackInterface() *net.Interface {
+	ift, err := net.Interfaces()
+	if err != nil {
+		return nil
+	}
+	for _, ifi := range ift {
+		if ifi.Flags&net.FlagLoopback == 0 || ifi.Flags&net.FlagUp == 0 {
+			continue
+		}
+		ifat, err := ifi.Addrs()
+		if err != nil {
+			continue
+		}
+		for _, ifa := range ifat {
+			switch ifa := ifa.(type) {
+			case *net.IPAddr:
+				if isLinkLocalUnicast(ifa.IP) {
+					return &ifi
+				}
+			case *net.IPNet:
+				if isLinkLocalUnicast(ifa.IP) {
+					return &ifi
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) {
+	if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
+		return nil, false
+	}
+	ifat, err := ifi.Addrs()
+	if err != nil {
+		return nil, false
+	}
+	for _, ifa := range ifat {
+		switch ifa := ifa.(type) {
+		case *net.IPAddr:
+			if isLinkLocalUnicast(ifa.IP) {
+				return ifa.IP, true
+			}
+		case *net.IPNet:
+			if isLinkLocalUnicast(ifa.IP) {
+				return ifa.IP, true
+			}
+		}
+	}
+	return nil, false
+}
+
+func connector(t *testing.T, network, addr string, done chan<- bool) {
+	defer func() { done <- true }()
+
+	c, err := net.Dial(network, addr)
+	if err != nil {
+		t.Errorf("net.Dial failed: %v", err)
+		return
+	}
+	c.Close()
+}
+
+func acceptor(t *testing.T, ln net.Listener, done chan<- bool) {
+	defer func() { done <- true }()
+
+	c, err := ln.Accept()
+	if err != nil {
+		t.Errorf("net.Listener.Accept failed: %v", err)
+		return
+	}
+	c.Close()
+}
+
+func transponder(t *testing.T, ln net.Listener, done chan<- bool) {
+	defer func() { done <- true }()
+
+	c, err := ln.Accept()
+	if err != nil {
+		t.Errorf("net.Listener.Accept failed: %v", err)
+		return
+	}
+	defer c.Close()
+
+	b := make([]byte, 128)
+	n, err := c.Read(b)
+	if err != nil {
+		t.Errorf("net.Conn.Read failed: %v", err)
+		return
+	}
+	if _, err := c.Write(b[:n]); err != nil {
+		t.Errorf("net.Conn.Write failed: %v", err)
+		return
+	}
+}

+ 154 - 0
ipv6/multicast_test.go

@@ -0,0 +1,154 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"net"
+	"os"
+	"runtime"
+	"testing"
+)
+
+func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
+	switch runtime.GOOS {
+	case "freebsd": // due to a bug on loopback marking
+		t.Skipf("not supported on %q", runtime.GOOS)
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	c, err := net.ListenPacket("udp6", "[ff02::114]:0") // see RFC 4727
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	_, port, err := net.SplitHostPort(c.LocalAddr().String())
+	if err != nil {
+		t.Fatalf("net.SplitHostPort failed: %v", err)
+	}
+	dst, err := net.ResolveUDPAddr("udp6", "[ff02::114]:"+port) // see RFC 4727
+	if err != nil {
+		t.Fatalf("net.ResolveUDPAddr failed: %v", err)
+	}
+
+	p := ipv6.NewPacketConn(c)
+	if err := p.JoinGroup(ifi, dst); err != nil {
+		t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
+	}
+	if err := p.SetMulticastInterface(ifi); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
+	}
+	if err := p.SetMulticastLoopback(true); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
+	}
+
+	cm := ipv6.ControlMessage{
+		TrafficClass: DiffServAF11 | CongestionExperienced,
+		IfIndex:      ifi.Index,
+	}
+	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
+
+	for i, toggle := range []bool{true, false, true} {
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
+		}
+		cm.HopLimit = i + 1
+		if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
+			t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+		if _, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+		}
+	}
+}
+
+func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	dst, err := net.ResolveIPAddr("ip6", "ff02::114") // see RFC 4727
+	if err != nil {
+		t.Fatalf("net.ResolveIPAddr failed: %v", err)
+	}
+
+	p := ipv6.NewPacketConn(c)
+	if err := p.JoinGroup(ifi, dst); err != nil {
+		t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
+	}
+	if err := p.SetMulticastInterface(ifi); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetMulticastInterface failed: %v", err)
+	}
+	if err := p.SetMulticastLoopback(true); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
+	}
+
+	cm := ipv6.ControlMessage{
+		TrafficClass: DiffServAF11 | CongestionExperienced,
+		IfIndex:      ifi.Index,
+	}
+	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
+
+	var f ipv6.ICMPFilter
+	f.SetAll(true)
+	f.Set(ipv6.ICMPTypeEchoReply, false)
+	if err := p.SetICMPFilter(&f); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
+	}
+
+	for i, toggle := range []bool{true, false, true} {
+		wb, err := (&icmpMessage{
+			Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+			Body: &icmpEcho{
+				ID: os.Getpid() & 0xffff, Seq: i + 1,
+				Data: []byte("HELLO-R-U-THERE"),
+			},
+		}).Marshal()
+		if err != nil {
+			t.Fatalf("icmpMessage.Marshal failed: %v", err)
+		}
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
+		}
+		cm.HopLimit = i + 1
+		if _, err := p.WriteTo(wb, &cm, dst); err != nil {
+			t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+		if n, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+			if m, err := parseICMPMessage(b[:n]); err != nil {
+				t.Fatalf("parseICMPMessage failed: %v", err)
+			} else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
+				t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)
+			}
+		}
+	}
+}

+ 185 - 0
ipv6/multicastlistener_test.go

@@ -0,0 +1,185 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"fmt"
+	"net"
+	"os"
+	"runtime"
+	"testing"
+)
+
+var udpMultipleGroupListenerTests = []net.Addr{
+	&net.UDPAddr{IP: net.ParseIP("ff02::114")}, // see RFC 4727
+	&net.UDPAddr{IP: net.ParseIP("ff02::1:114")},
+	&net.UDPAddr{IP: net.ParseIP("ff02::2:114")},
+}
+
+func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	for _, gaddr := range udpMultipleGroupListenerTests {
+		c, err := net.ListenPacket("udp6", "[::]:0") // wildcard address with non-reusable port
+		if err != nil {
+			t.Fatalf("net.ListenPacket failed: %v", err)
+		}
+		defer c.Close()
+
+		p := ipv6.NewPacketConn(c)
+		var mift []*net.Interface
+
+		ift, err := net.Interfaces()
+		if err != nil {
+			t.Fatalf("net.Interfaces failed: %v", err)
+		}
+		for i, ifi := range ift {
+			if _, ok := isMulticastAvailable(&ifi); !ok {
+				continue
+			}
+			if err := p.JoinGroup(&ifi, gaddr); err != nil {
+				t.Fatalf("ipv6.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
+			}
+			mift = append(mift, &ift[i])
+		}
+		for _, ifi := range mift {
+			if err := p.LeaveGroup(ifi, gaddr); err != nil {
+				t.Fatalf("ipv6.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
+			}
+		}
+	}
+}
+
+func TestUDPMultipleConnWithMultipleGroupListeners(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	for _, gaddr := range udpMultipleGroupListenerTests {
+		c1, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port
+		if err != nil {
+			t.Fatalf("net.ListenPacket failed: %v", err)
+		}
+		defer c1.Close()
+
+		c2, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port
+		if err != nil {
+			t.Fatalf("net.ListenPacket failed: %v", err)
+		}
+		defer c2.Close()
+
+		var ps [2]*ipv6.PacketConn
+		ps[0] = ipv6.NewPacketConn(c1)
+		ps[1] = ipv6.NewPacketConn(c2)
+		var mift []*net.Interface
+
+		ift, err := net.Interfaces()
+		if err != nil {
+			t.Fatalf("net.Interfaces failed: %v", err)
+		}
+		for i, ifi := range ift {
+			if _, ok := isMulticastAvailable(&ifi); !ok {
+				continue
+			}
+			for _, p := range ps {
+				if err := p.JoinGroup(&ifi, gaddr); err != nil {
+					t.Fatalf("ipv6.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err)
+				}
+			}
+			mift = append(mift, &ift[i])
+		}
+		for _, ifi := range mift {
+			for _, p := range ps {
+				if err := p.LeaveGroup(ifi, gaddr); err != nil {
+					t.Fatalf("ipv6.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err)
+				}
+			}
+		}
+	}
+}
+
+func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
+	type ml struct {
+		c   *ipv6.PacketConn
+		ifi *net.Interface
+	}
+	var mlt []*ml
+
+	ift, err := net.Interfaces()
+	if err != nil {
+		t.Fatalf("net.Interfaces failed: %v", err)
+	}
+	for i, ifi := range ift {
+		ip, ok := isMulticastAvailable(&ifi)
+		if !ok {
+			continue
+		}
+		c, err := net.ListenPacket("udp6", fmt.Sprintf("[%s%%%s]:1024", ip.String(), ifi.Name)) // unicast address with non-reusable port
+		if err != nil {
+			t.Fatalf("net.ListenPacket with %v failed: %v", ip, err)
+		}
+		defer c.Close()
+		p := ipv6.NewPacketConn(c)
+		if err := p.JoinGroup(&ifi, gaddr); err != nil {
+			t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
+		}
+		mlt = append(mlt, &ml{p, &ift[i]})
+	}
+	for _, m := range mlt {
+		if err := m.c.LeaveGroup(m.ifi, gaddr); err != nil {
+			t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", m.ifi, err)
+		}
+	}
+}
+
+func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+
+	c, err := net.ListenPacket("ip6:ipv6-icmp", "::")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	p := ipv6.NewPacketConn(c)
+	gaddr := &net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727
+	var mift []*net.Interface
+
+	ift, err := net.Interfaces()
+	if err != nil {
+		t.Fatalf("net.Interfaces failed: %v", err)
+	}
+	for i, ifi := range ift {
+		if _, ok := isMulticastAvailable(&ifi); !ok {
+			continue
+		}
+		if err := p.JoinGroup(&ifi, gaddr); err != nil {
+			t.Fatalf("ipv6.PacketConn.JoinGroup on %v failed: %v", ifi, err)
+		}
+		mift = append(mift, &ift[i])
+	}
+	for _, ifi := range mift {
+		if err := p.LeaveGroup(ifi, gaddr); err != nil {
+			t.Fatalf("ipv6.PacketConn.LeaveGroup on %v failed: %v", ifi, err)
+		}
+	}
+}

+ 73 - 0
ipv6/multicastsockopt_test.go

@@ -0,0 +1,73 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"net"
+	"os"
+	"runtime"
+	"testing"
+)
+
+var packetConnMulticastSocketOptionTests = []struct {
+	net, proto, addr string
+	gaddr            net.Addr
+}{
+	{"udp6", "", "[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}}, // see RFC 4727
+	{"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff02::114")}}, // see RFC 4727
+}
+
+func TestPacketConnMulticastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		t.Skipf("not available on %q", runtime.GOOS)
+	}
+
+	for _, tt := range packetConnMulticastSocketOptionTests {
+		if tt.net == "ip6" && os.Getuid() != 0 {
+			t.Skip("must be root")
+		}
+		c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+		if err != nil {
+			t.Fatalf("net.ListenPacket failed: %v", err)
+		}
+		defer c.Close()
+
+		p := ipv6.NewPacketConn(c)
+
+		hoplim := 255
+		if err := p.SetMulticastHopLimit(hoplim); err != nil {
+			t.Fatalf("ipv6.PacketConn.SetMulticastHopLimit failed: %v", err)
+		}
+		if v, err := p.MulticastHopLimit(); err != nil {
+			t.Fatalf("ipv6.PacketConn.MulticastHopLimit failed: %v", err)
+		} else if v != hoplim {
+			t.Fatalf("got unexpected multicast hop limit %v; expected %v", v, hoplim)
+		}
+
+		for _, toggle := range []bool{true, false} {
+			if err := p.SetMulticastLoopback(toggle); err != nil {
+				t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err)
+			}
+			if v, err := p.MulticastLoopback(); err != nil {
+				t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err)
+			} else if v != toggle {
+				t.Fatalf("got unexpected multicast loopback %v; expected %v", v, toggle)
+			}
+		}
+
+		if err := p.JoinGroup(ifi, tt.gaddr); err != nil {
+			t.Fatalf("ipv6.PacketConn.JoinGroup(%v, %v) failed: %v", ifi, tt.gaddr, err)
+		}
+		if err := p.LeaveGroup(ifi, tt.gaddr); err != nil {
+			t.Fatalf("ipv6.PacketConn.LeaveGroup(%v, %v) failed: %v", ifi, tt.gaddr, err)
+		}
+	}
+}

+ 15 - 0
ipv6/payload.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.
+
+package ipv6
+
+import "net"
+
+// A payloadHandler represents the IPv6 datagram payload handler.
+type payloadHandler struct {
+	net.PacketConn
+	rawOpt
+}
+
+func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil }

+ 70 - 0
ipv6/payload_cmsg.go

@@ -0,0 +1,70 @@
+// 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 !plan9,!windows
+
+package ipv6
+
+import (
+	"net"
+	"syscall"
+)
+
+// ReadFrom reads a payload of the received IPv6 datagram, from the
+// endpoint c, copying the payload into b.  It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+	if !c.ok() {
+		return 0, nil, nil, syscall.EINVAL
+	}
+	oob := newControlMessage(&c.rawOpt)
+	var oobn int
+	switch c := c.PacketConn.(type) {
+	case *net.UDPConn:
+		if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil {
+			return 0, nil, nil, err
+		}
+	case *net.IPConn:
+		if n, oobn, _, src, err = c.ReadMsgIP(b, oob); err != nil {
+			return 0, nil, nil, err
+		}
+	default:
+		return 0, nil, nil, errInvalidConnType
+	}
+	if cm, err = parseControlMessage(oob[:oobn]); err != nil {
+		return 0, nil, nil, err
+	}
+	if cm != nil {
+		cm.Src = netAddrToIP16(src)
+	}
+	return
+}
+
+// WriteTo writes a payload of the IPv6 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b.  It
+// returns the number of bytes written.  The control message cm allows
+// the IPv6 header fields and the datagram path to be specified.  The
+// cm may be nil if control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	oob := marshalControlMessage(cm)
+	if dst == nil {
+		return 0, errMissingAddress
+	}
+	switch c := c.PacketConn.(type) {
+	case *net.UDPConn:
+		n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr))
+	case *net.IPConn:
+		n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr))
+	default:
+		return 0, errInvalidConnType
+	}
+	if err != nil {
+		return 0, err
+	}
+	return
+}

+ 41 - 0
ipv6/payload_noncmsg.go

@@ -0,0 +1,41 @@
+// 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 plan9 windows
+
+package ipv6
+
+import (
+	"net"
+	"syscall"
+)
+
+// ReadFrom reads a payload of the received IPv6 datagram, from the
+// endpoint c, copying the payload into b.  It returns the number of
+// bytes copied into b, the control message cm and the source address
+// src of the received datagram.
+func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) {
+	if !c.ok() {
+		return 0, nil, nil, syscall.EINVAL
+	}
+	if n, src, err = c.PacketConn.ReadFrom(b); err != nil {
+		return 0, nil, nil, err
+	}
+	return
+}
+
+// WriteTo writes a payload of the IPv6 datagram, to the destination
+// address dst through the endpoint c, copying the payload from b.  It
+// returns the number of bytes written.  The control message cm allows
+// the IPv6 header fields and the datagram path to be specified.  The
+// cm may be nil if control of the outgoing datagram is not required.
+func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	if dst == nil {
+		return 0, errMissingAddress
+	}
+	return c.PacketConn.WriteTo(b, dst)
+}

+ 66 - 0
ipv6/sockopt_rfc2292_darwin.go

@@ -0,0 +1,66 @@
+// 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 (
+	"os"
+	"syscall"
+)
+
+func ipv6ReceiveTrafficClass(fd int) (bool, error) {
+	return false, errNotSupported
+}
+
+func setIPv6ReceiveTrafficClass(fd int, v bool) error {
+	return errNotSupported
+}
+
+func ipv6ReceiveHopLimit(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292HOPLIMIT)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceiveHopLimit(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292HOPLIMIT, boolint(v)))
+}
+
+func ipv6ReceivePacketInfo(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292PKTINFO)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceivePacketInfo(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_2292PKTINFO, boolint(v)))
+}
+
+func ipv6PathMTU(fd int) (int, error) {
+	return 0, errNotSupported
+}
+
+func ipv6ReceivePathMTU(fd int) (bool, error) {
+	return false, errNotSupported
+}
+
+func setIPv6ReceivePathMTU(fd int, v bool) error {
+	return errNotSupported
+}
+
+func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
+	v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER)
+	if err != nil {
+		return nil, os.NewSyscallError("getsockopt", err)
+	}
+	return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil
+}
+
+func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER, &f.rawICMPFilter.ICMPv6Filter))
+}

+ 19 - 0
ipv6/sockopt_rfc3493_bsd.go

@@ -0,0 +1,19 @@
+// 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 netbsd openbsd
+
+package ipv6
+
+import (
+	"os"
+	"syscall"
+)
+
+func setIPv6Checksum(fd int, on bool, offset int) error {
+	if !on {
+		offset = -1
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM, offset))
+}

+ 17 - 0
ipv6/sockopt_rfc3493_linux.go

@@ -0,0 +1,17 @@
+// 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 (
+	"os"
+	"syscall"
+)
+
+func setIPv6Checksum(fd int, on bool, offset int) error {
+	if !on {
+		offset = -1
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolReserved, syscall.IPV6_CHECKSUM, offset))
+}

+ 114 - 0
ipv6/sockopt_rfc3493_unix.go

@@ -0,0 +1,114 @@
+// 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 (
+	"net"
+	"os"
+	"syscall"
+)
+
+func ipv6TrafficClass(fd int) (int, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS)
+	if err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return v, nil
+}
+
+func setIPv6TrafficClass(fd, v int) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_TCLASS, v))
+}
+
+func ipv6HopLimit(fd int) (int, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS)
+	if err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return v, nil
+}
+
+func setIPv6HopLimit(fd, v int) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_UNICAST_HOPS, v))
+}
+
+func ipv6Checksum(fd int) (bool, int, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_CHECKSUM)
+	if err != nil {
+		return false, 0, os.NewSyscallError("getsockopt", err)
+	}
+	on := true
+	if v == -1 {
+		on = false
+	}
+	return on, v, nil
+}
+
+func ipv6MulticastHopLimit(fd int) (int, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS)
+	if err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return v, nil
+}
+
+func setIPv6MulticastHopLimit(fd, v int) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_HOPS, v))
+}
+
+func ipv6MulticastInterface(fd int) (*net.Interface, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF)
+	if err != nil {
+		return nil, os.NewSyscallError("getsockopt", err)
+	}
+	if v == 0 {
+		return nil, nil
+	}
+	ifi, err := net.InterfaceByIndex(v)
+	if err != nil {
+		return nil, err
+	}
+	return ifi, nil
+}
+
+func setIPv6MulticastInterface(fd int, ifi *net.Interface) error {
+	var v int
+	if ifi != nil {
+		v = ifi.Index
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_IF, v))
+}
+
+func ipv6MulticastLoopback(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6MulticastLoopback(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_MULTICAST_LOOP, boolint(v)))
+}
+
+func joinIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
+	mreq := syscall.IPv6Mreq{}
+	copy(mreq.Multiaddr[:], grp)
+	if ifi != nil {
+		mreq.Interface = uint32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_JOIN_GROUP, &mreq))
+}
+
+func leaveIPv6Group(fd int, ifi *net.Interface, grp net.IP) error {
+	mreq := syscall.IPv6Mreq{}
+	copy(mreq.Multiaddr[:], grp)
+	if ifi != nil {
+		mreq.Interface = uint32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd, ianaProtocolIPv6, syscall.IPV6_LEAVE_GROUP, &mreq))
+}

+ 116 - 0
ipv6/sockopt_rfc3493_windows.go

@@ -0,0 +1,116 @@
+// 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"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func ipv6TrafficClass(fd syscall.Handle) (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EWINDOWS
+}
+
+func setIPv6TrafficClass(fd syscall.Handle, v int) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}
+
+func ipv6HopLimit(fd syscall.Handle) (int, error) {
+	var v int32
+	l := int32(4)
+	if err := syscall.Getsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_UNICAST_HOPS), (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return int(v), nil
+}
+
+func setIPv6HopLimit(fd syscall.Handle, v int) error {
+	vv := int32(v)
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_UNICAST_HOPS), (*byte)(unsafe.Pointer(&vv)), 4))
+}
+
+func ipv6Checksum(fd syscall.Handle) (bool, int, error) {
+	// TODO(mikio): Implement this
+	return false, 0, syscall.EWINDOWS
+}
+
+func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) {
+	var v int32
+	l := int32(4)
+	if err := syscall.Getsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_MULTICAST_HOPS), (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return int(v), nil
+}
+
+func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error {
+	vv := int32(v)
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_MULTICAST_HOPS), (*byte)(unsafe.Pointer(&vv)), 4))
+}
+
+func ipv6MulticastInterface(fd syscall.Handle) (*net.Interface, error) {
+	var v int32
+	l := int32(4)
+	if err := syscall.Getsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_MULTICAST_IF), (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+		return nil, os.NewSyscallError("getsockopt", err)
+	}
+	if v == 0 {
+		return nil, nil
+	}
+	ifi, err := net.InterfaceByIndex(int(v))
+	if err != nil {
+		return nil, err
+	}
+	return ifi, nil
+}
+
+func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error {
+	var v int32
+	if ifi != nil {
+		v = int32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_MULTICAST_IF), (*byte)(unsafe.Pointer(&v)), 4))
+}
+
+func ipv6MulticastLoopback(fd syscall.Handle) (bool, error) {
+	var v int32
+	l := int32(4)
+	if err := syscall.Getsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&v)), &l); err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error {
+	vv := int32(boolint(v))
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4))
+}
+
+func joinIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
+	mreq := syscall.IPv6Mreq{}
+	copy(mreq.Multiaddr[:], grp)
+	if ifi != nil {
+		mreq.Interface = uint32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_JOIN_GROUP), (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+}
+
+func leaveIPv6Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error {
+	mreq := syscall.IPv6Mreq{}
+	copy(mreq.Multiaddr[:], grp)
+	if ifi != nil {
+		mreq.Interface = uint32(ifi.Index)
+	}
+	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, int32(ianaProtocolIPv6), int32(syscall.IPV6_LEAVE_GROUP), (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq))))
+}
+
+func setIPv6Checksum(fd syscall.Handle, on bool, offset int) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}

+ 44 - 0
ipv6/sockopt_rfc3542_bsd.go

@@ -0,0 +1,44 @@
+// 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 (
+	"os"
+	"syscall"
+)
+
+func ipv6PathMTU(fd int) (int, error) {
+	v, err := syscall.GetsockoptIPv6MTUInfo(fd, ianaProtocolIPv6, syscall.IPV6_PATHMTU)
+	if err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return int(v.Mtu), nil
+}
+
+func ipv6ReceivePathMTU(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPATHMTU)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceivePathMTU(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPATHMTU, boolint(v)))
+}
+
+func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
+	v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER)
+	if err != nil {
+		return nil, os.NewSyscallError("getsockopt", err)
+	}
+	return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil
+}
+
+func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMP6_FILTER, &f.rawICMPFilter.ICMPv6Filter))
+}

+ 42 - 0
ipv6/sockopt_rfc3542_linux.go

@@ -0,0 +1,42 @@
+// 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 (
+	"os"
+	"syscall"
+)
+
+func ipv6PathMTU(fd int) (int, error) {
+	v, err := syscall.GetsockoptIPv6MTUInfo(fd, ianaProtocolIPv6, syscall_IPV6_PATHMTU)
+	if err != nil {
+		return 0, os.NewSyscallError("getsockopt", err)
+	}
+	return int(v.Mtu), nil
+}
+
+func ipv6ReceivePathMTU(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall_IPV6_RECVPATHMTU)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceivePathMTU(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall_IPV6_RECVPATHMTU, boolint(v)))
+}
+
+func ipv6ICMPFilter(fd int) (*ICMPFilter, error) {
+	v, err := syscall.GetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMPV6_FILTER)
+	if err != nil {
+		return nil, os.NewSyscallError("getsockopt", err)
+	}
+	return &ICMPFilter{rawICMPFilter: rawICMPFilter{*v}}, nil
+}
+
+func setIPv6ICMPFilter(fd int, f *ICMPFilter) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptICMPv6Filter(fd, ianaProtocolIPv6ICMP, syscall.ICMPV6_FILTER, &f.rawICMPFilter.ICMPv6Filter))
+}

+ 12 - 0
ipv6/sockopt_rfc3542_plan9.go

@@ -0,0 +1,12 @@
+// 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 "syscall"
+
+func ipv6PathMTU(fd int) (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EPLAN9
+}

+ 48 - 0
ipv6/sockopt_rfc3542_unix.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 linux netbsd openbsd
+
+package ipv6
+
+import (
+	"os"
+	"syscall"
+)
+
+func ipv6ReceiveTrafficClass(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceiveTrafficClass(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVTCLASS, boolint(v)))
+}
+
+func ipv6ReceiveHopLimit(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceiveHopLimit(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVHOPLIMIT, boolint(v)))
+}
+
+func ipv6ReceivePacketInfo(fd int) (bool, error) {
+	v, err := syscall.GetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO)
+	if err != nil {
+		return false, os.NewSyscallError("getsockopt", err)
+	}
+	return v == 1, nil
+}
+
+func setIPv6ReceivePacketInfo(fd int, v bool) error {
+	return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIPv6, syscall.IPV6_RECVPKTINFO, boolint(v)))
+}

+ 62 - 0
ipv6/sockopt_rfc3542_windows.go

@@ -0,0 +1,62 @@
+// 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 "syscall"
+
+func ipv6ReceiveTrafficClass(fd syscall.Handle) (bool, error) {
+	// TODO(mikio): Implement this
+	return false, syscall.EWINDOWS
+}
+
+func setIPv6ReceiveTrafficClass(fd syscall.Handle, v bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}
+
+func ipv6ReceiveHopLimit(fd syscall.Handle) (bool, error) {
+	// TODO(mikio): Implement this
+	return false, syscall.EWINDOWS
+}
+
+func setIPv6ReceiveHopLimit(fd syscall.Handle, v bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}
+
+func ipv6ReceivePacketInfo(fd syscall.Handle) (bool, error) {
+	// TODO(mikio): Implement this
+	return false, syscall.EWINDOWS
+}
+
+func setIPv6ReceivePacketInfo(fd syscall.Handle, v bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}
+
+func ipv6PathMTU(fd syscall.Handle) (int, error) {
+	// TODO(mikio): Implement this
+	return 0, syscall.EWINDOWS
+}
+
+func ipv6ReceivePathMTU(fd syscall.Handle) (bool, error) {
+	// TODO(mikio): Implement this
+	return false, syscall.EWINDOWS
+}
+
+func setIPv6ReceivePathMTU(fd syscall.Handle, v bool) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}
+
+func ipv6ICMPFilter(fd syscall.Handle) (*ICMPFilter, error) {
+	// TODO(mikio): Implement this
+	return nil, syscall.EWINDOWS
+}
+
+func setIPv6ICMPFilter(fd syscall.Handle, f *ICMPFilter) error {
+	// TODO(mikio): Implement this
+	return syscall.EWINDOWS
+}

+ 118 - 0
ipv6/sockopt_test.go

@@ -0,0 +1,118 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"net"
+	"os"
+	"runtime"
+	"testing"
+)
+
+var condFatalf = func() func(*testing.T, string, ...interface{}) {
+	// A few APIs are not implemented yet on some platforms.
+	switch runtime.GOOS {
+	case "darwin", "plan9", "windows":
+		return (*testing.T).Logf
+	}
+	return (*testing.T).Fatalf
+}()
+
+func TestConnInitiatorPathMTU(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	ln, err := net.Listen("tcp6", "[::1]:0")
+	if err != nil {
+		t.Fatalf("net.Listen failed: %v", err)
+	}
+	defer ln.Close()
+
+	done := make(chan bool)
+	go acceptor(t, ln, done)
+
+	c, err := net.Dial("tcp6", ln.Addr().String())
+	if err != nil {
+		t.Fatalf("net.Dial failed: %v", err)
+	}
+	defer c.Close()
+
+	if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil {
+		condFatalf(t, "ipv6.Conn.PathMTU failed: %v", err)
+	} else {
+		t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu)
+	}
+
+	<-done
+}
+
+func TestConnResponderPathMTU(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	ln, err := net.Listen("tcp6", "[::1]:0")
+	if err != nil {
+		t.Fatalf("net.Listen failed: %v", err)
+	}
+	defer ln.Close()
+
+	done := make(chan bool)
+	go connector(t, "tcp6", ln.Addr().String(), done)
+
+	c, err := ln.Accept()
+	if err != nil {
+		t.Fatalf("net.Accept failed: %v", err)
+	}
+	defer c.Close()
+
+	if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil {
+		condFatalf(t, "ipv6.Conn.PathMTU failed: %v", err)
+	} else {
+		t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu)
+	}
+
+	<-done
+}
+
+func TestPacketConnChecksum(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+
+	c, err := net.ListenPacket("ip6:89", "::") // OSPF for IPv6
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	p := ipv6.NewPacketConn(c)
+	offset := 12 // see RFC 5340
+
+	for _, toggle := range []bool{false, true} {
+		if err := p.SetChecksum(toggle, offset); err != nil {
+			if toggle {
+				t.Fatalf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err)
+			} else {
+				// Some platforms never allow to disable the kernel
+				// checksum processing.
+				t.Logf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err)
+			}
+		}
+		if on, offset, err := p.Checksum(); err != nil {
+			t.Fatalf("ipv6.PacketConn.Checksum failed: %v", err)
+		} else {
+			t.Logf("kernel checksum processing enabled=%v, offset=%v", on, offset)
+		}
+	}
+}

+ 195 - 0
ipv6/unicast_test.go

@@ -0,0 +1,195 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"net"
+	"os"
+	"runtime"
+	"testing"
+)
+
+func benchmarkUDPListener() (net.PacketConn, net.Addr, error) {
+	c, err := net.ListenPacket("udp6", "[::1]:0")
+	if err != nil {
+		return nil, nil, err
+	}
+	dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
+	if err != nil {
+		c.Close()
+		return nil, nil, err
+	}
+	return c, dst, nil
+}
+
+func BenchmarkReadWriteNetUDP(b *testing.B) {
+	c, dst, err := benchmarkUDPListener()
+	if err != nil {
+		b.Fatalf("benchmarkUDPListener failed: %v", err)
+	}
+	defer c.Close()
+
+	for i := 0; i < b.N; i++ {
+		benchmarkReadWriteNetUDP(b, c, dst)
+	}
+}
+
+func benchmarkReadWriteNetUDP(b *testing.B, c net.PacketConn, dst net.Addr) {
+	if _, err := c.WriteTo([]byte("HELLO-R-U-THERE"), dst); err != nil {
+		b.Fatalf("net.PacketConn.WriteTo failed: %v", err)
+	}
+	rb := make([]byte, 128)
+	if _, _, err := c.ReadFrom(rb); err != nil {
+		b.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
+	}
+}
+
+func BenchmarkReadWriteIPv6UDP(b *testing.B) {
+	c, dst, err := benchmarkUDPListener()
+	if err != nil {
+		b.Fatalf("benchmarkUDPListener failed: %v", err)
+	}
+	defer c.Close()
+
+	p := ipv6.NewPacketConn(c)
+	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
+	if err := p.SetControlMessage(cf, true); err != nil {
+		b.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
+	}
+	ifi := loopbackInterface()
+
+	for i := 0; i < b.N; i++ {
+		benchmarkReadWriteIPv6UDP(b, p, dst, ifi)
+	}
+}
+
+func benchmarkReadWriteIPv6UDP(b *testing.B, p *ipv6.PacketConn, dst net.Addr, ifi *net.Interface) {
+	cm := ipv6.ControlMessage{
+		TrafficClass: DiffServAF11 | CongestionExperienced,
+		HopLimit:     1,
+	}
+	if ifi != nil {
+		cm.IfIndex = ifi.Index
+	}
+	if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
+		b.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
+	}
+	rb := make([]byte, 128)
+	if _, _, _, err := p.ReadFrom(rb); err != nil {
+		b.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
+	}
+}
+
+func TestPacketConnReadWriteUnicastUDP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	c, err := net.ListenPacket("udp6", "[::1]:0")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	dst, err := net.ResolveUDPAddr("udp6", c.LocalAddr().String())
+	if err != nil {
+		t.Fatalf("net.ResolveUDPAddr failed: %v", err)
+	}
+
+	p := ipv6.NewPacketConn(c)
+	cm := ipv6.ControlMessage{
+		TrafficClass: DiffServAF11 | CongestionExperienced,
+	}
+	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
+	ifi := loopbackInterface()
+	if ifi != nil {
+		cm.IfIndex = ifi.Index
+	}
+
+	for i, toggle := range []bool{true, false, true} {
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
+		}
+		cm.HopLimit = i + 1
+		if _, err := p.WriteTo([]byte("HELLO-R-U-THERE"), &cm, dst); err != nil {
+			t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+		if _, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+		}
+	}
+}
+
+func TestPacketConnReadWriteUnicastICMP(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+	if os.Getuid() != 0 {
+		t.Skip("must be root")
+	}
+
+	c, err := net.ListenPacket("ip6:ipv6-icmp", "::1")
+	if err != nil {
+		t.Fatalf("net.ListenPacket failed: %v", err)
+	}
+	defer c.Close()
+
+	dst, err := net.ResolveIPAddr("ip6", "::1")
+	if err != nil {
+		t.Fatalf("net.ResolveIPAddr failed: %v", err)
+	}
+
+	p := ipv6.NewPacketConn(c)
+	cm := ipv6.ControlMessage{TrafficClass: DiffServAF11 | CongestionExperienced}
+	cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagInterface | ipv6.FlagPathMTU
+	ifi := loopbackInterface()
+	if ifi != nil {
+		cm.IfIndex = ifi.Index
+	}
+
+	var f ipv6.ICMPFilter
+	f.SetAll(true)
+	f.Set(ipv6.ICMPTypeEchoReply, false)
+	if err := p.SetICMPFilter(&f); err != nil {
+		t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err)
+	}
+
+	for i, toggle := range []bool{true, false, true} {
+		wb, err := (&icmpMessage{
+			Type: ipv6.ICMPTypeEchoRequest, Code: 0,
+			Body: &icmpEcho{
+				ID: os.Getpid() & 0xffff, Seq: i + 1,
+				Data: []byte("HELLO-R-U-THERE"),
+			},
+		}).Marshal()
+		if err != nil {
+			t.Fatalf("icmpMessage.Marshal failed: %v", err)
+		}
+		if err := p.SetControlMessage(cf, toggle); err != nil {
+			t.Fatalf("ipv6.PacketConn.SetControlMessage failed: %v", err)
+		}
+		cm.HopLimit = i + 1
+		if _, err := p.WriteTo(wb, &cm, dst); err != nil {
+			t.Fatalf("ipv6.PacketConn.WriteTo failed: %v", err)
+		}
+		b := make([]byte, 128)
+		if n, cm, _, err := p.ReadFrom(b); err != nil {
+			t.Fatalf("ipv6.PacketConn.ReadFrom failed: %v", err)
+		} else {
+			t.Logf("rcvd cmsg: %v", cm)
+			if m, err := parseICMPMessage(b[:n]); err != nil {
+				t.Fatalf("parseICMPMessage failed: %v", err)
+			} else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 {
+				t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0)
+			}
+		}
+	}
+}

+ 95 - 0
ipv6/unicastsockopt_test.go

@@ -0,0 +1,95 @@
+// 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_test
+
+import (
+	"code.google.com/p/go.net/ipv6"
+	"net"
+	"os"
+	"runtime"
+	"testing"
+)
+
+func TestConnUnicastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	ln, err := net.Listen("tcp6", "[::1]:0")
+	if err != nil {
+		t.Fatalf("net.Listen failed: %v", err)
+	}
+	defer ln.Close()
+
+	done := make(chan bool)
+	go acceptor(t, ln, done)
+
+	c, err := net.Dial("tcp6", ln.Addr().String())
+	if err != nil {
+		t.Fatalf("net.Dial failed: %v", err)
+	}
+	defer c.Close()
+
+	testUnicastSocketOptions(t, ipv6.NewConn(c))
+
+	<-done
+}
+
+var packetConnUnicastSocketOptionTests = []struct {
+	net, proto, addr string
+}{
+	{"udp6", "", "[::1]:0"},
+	{"ip6", ":ipv6-icmp", "::1"},
+}
+
+func TestPacketConnUnicastSocketOptions(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("not supported on %q", runtime.GOOS)
+	}
+
+	for _, tt := range packetConnUnicastSocketOptionTests {
+		if tt.net == "ip6" && os.Getuid() != 0 {
+			t.Skip("must be root")
+		}
+		c, err := net.ListenPacket(tt.net+tt.proto, tt.addr)
+		if err != nil {
+			t.Fatalf("net.ListenPacket(%q, %q) failed: %v", tt.net+tt.proto, tt.addr, err)
+		}
+		defer c.Close()
+
+		testUnicastSocketOptions(t, ipv6.NewPacketConn(c))
+	}
+}
+
+type testIPv6UnicastConn interface {
+	TrafficClass() (int, error)
+	SetTrafficClass(int) error
+	HopLimit() (int, error)
+	SetHopLimit(int) error
+}
+
+func testUnicastSocketOptions(t *testing.T, c testIPv6UnicastConn) {
+	tclass := DiffServCS0 | NotECNTransport
+	if err := c.SetTrafficClass(tclass); err != nil {
+		t.Fatalf("ipv6.Conn.SetTrafficClass failed: %v", err)
+	}
+	if v, err := c.TrafficClass(); err != nil {
+		t.Fatalf("ipv6.Conn.TrafficClass failed: %v", err)
+	} else if v != tclass {
+		t.Fatalf("got unexpected traffic class %v; expected %v", v, tclass)
+	}
+
+	hoplim := 255
+	if err := c.SetHopLimit(hoplim); err != nil {
+		t.Fatalf("ipv6.Conn.SetHopLimit failed: %v", err)
+	}
+	if v, err := c.HopLimit(); err != nil {
+		t.Fatalf("ipv6.Conn.HopLimit failed: %v", err)
+	} else if v != hoplim {
+		t.Fatalf("got unexpected hop limit %v; expected %v", v, hoplim)
+	}
+}