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
diff --git a/ipv6/control.go b/ipv6/control.go
new file mode 100644
index 0000000..be06dd6
--- /dev/null
+++ b/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)
+}
diff --git a/ipv6/control_rfc2292_darwin.go b/ipv6/control_rfc2292_darwin.go
new file mode 100644
index 0000000..f09ad7b
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/control_rfc3542_bsd.go b/ipv6/control_rfc3542_bsd.go
new file mode 100644
index 0000000..0b42c0b
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/control_rfc3542_linux.go b/ipv6/control_rfc3542_linux.go
new file mode 100644
index 0000000..3e73f3b
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/control_rfc3542_plan9.go b/ipv6/control_rfc3542_plan9.go
new file mode 100644
index 0000000..052b229
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/control_rfc3542_windows.go b/ipv6/control_rfc3542_windows.go
new file mode 100644
index 0000000..b4d53fb
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/control_test.go b/ipv6/control_test.go
new file mode 100644
index 0000000..db66f8e
--- /dev/null
+++ b/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)
+ }
+}
diff --git a/ipv6/dgramopt_plan9.go b/ipv6/dgramopt_plan9.go
new file mode 100644
index 0000000..4c26be2
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/dgramopt_posix.go b/ipv6/dgramopt_posix.go
new file mode 100644
index 0000000..c52f48d
--- /dev/null
+++ b/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)
+}
diff --git a/ipv6/doc.go b/ipv6/doc.go
new file mode 100644
index 0000000..30201f8
--- /dev/null
+++ b/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
diff --git a/ipv6/endpoint.go b/ipv6/endpoint.go
new file mode 100644
index 0000000..aa19587
--- /dev/null
+++ b/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},
+ }
+}
diff --git a/ipv6/gen.go b/ipv6/gen.go
new file mode 100644
index 0000000..de529ad
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/genericopt_plan9.go b/ipv6/genericopt_plan9.go
new file mode 100644
index 0000000..6108443
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/genericopt_posix.go b/ipv6/genericopt_posix.go
new file mode 100644
index 0000000..a3a9af9
--- /dev/null
+++ b/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)
+}
diff --git a/ipv6/gentest.go b/ipv6/gentest.go
new file mode 100644
index 0000000..fddbfd1
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/helper.go b/ipv6/helper.go
new file mode 100644
index 0000000..ec87a0f
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/helper_plan9.go b/ipv6/helper_plan9.go
new file mode 100644
index 0000000..9f12a3a
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/helper_unix.go b/ipv6/helper_unix.go
new file mode 100644
index 0000000..6ad8db4
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/helper_windows.go b/ipv6/helper_windows.go
new file mode 100644
index 0000000..28c401b
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/iana.go b/ipv6/iana.go
new file mode 100644
index 0000000..c888cf2
--- /dev/null
+++ b/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
+)
diff --git a/ipv6/iana_test.go b/ipv6/iana_test.go
new file mode 100644
index 0000000..7b6bb85
--- /dev/null
+++ b/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)
+)
diff --git a/ipv6/icmp.go b/ipv6/icmp.go
new file mode 100644
index 0000000..9fb6a48
--- /dev/null
+++ b/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)
+}
diff --git a/ipv6/icmp_bsd.go b/ipv6/icmp_bsd.go
new file mode 100644
index 0000000..2096079
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/icmp_linux.go b/ipv6/icmp_linux.go
new file mode 100644
index 0000000..f60372b
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/icmp_plan9.go b/ipv6/icmp_plan9.go
new file mode 100644
index 0000000..b97c828
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/icmp_test.go b/ipv6/icmp_test.go
new file mode 100644
index 0000000..9727829
--- /dev/null
+++ b/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)
+ }
+}
diff --git a/ipv6/icmp_windows.go b/ipv6/icmp_windows.go
new file mode 100644
index 0000000..b97c828
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/mockicmp_test.go b/ipv6/mockicmp_test.go
new file mode 100644
index 0000000..13ee03d
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/mocktransponder_test.go b/ipv6/mocktransponder_test.go
new file mode 100644
index 0000000..2da5464
--- /dev/null
+++ b/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
+ }
+}
diff --git a/ipv6/multicast_test.go b/ipv6/multicast_test.go
new file mode 100644
index 0000000..395f8f5
--- /dev/null
+++ b/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)
+ }
+ }
+ }
+}
diff --git a/ipv6/multicastlistener_test.go b/ipv6/multicastlistener_test.go
new file mode 100644
index 0000000..61cf357
--- /dev/null
+++ b/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)
+ }
+ }
+}
diff --git a/ipv6/multicastsockopt_test.go b/ipv6/multicastsockopt_test.go
new file mode 100644
index 0000000..21fc32c
--- /dev/null
+++ b/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)
+ }
+ }
+}
diff --git a/ipv6/payload.go b/ipv6/payload.go
new file mode 100644
index 0000000..529b20b
--- /dev/null
+++ b/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 }
diff --git a/ipv6/payload_cmsg.go b/ipv6/payload_cmsg.go
new file mode 100644
index 0000000..658467a
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/payload_noncmsg.go b/ipv6/payload_noncmsg.go
new file mode 100644
index 0000000..dd0630a
--- /dev/null
+++ b/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)
+}
diff --git a/ipv6/sockopt_rfc2292_darwin.go b/ipv6/sockopt_rfc2292_darwin.go
new file mode 100644
index 0000000..f21802c
--- /dev/null
+++ b/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))
+}
diff --git a/ipv6/sockopt_rfc3493_bsd.go b/ipv6/sockopt_rfc3493_bsd.go
new file mode 100644
index 0000000..aa5e6e3
--- /dev/null
+++ b/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))
+}
diff --git a/ipv6/sockopt_rfc3493_linux.go b/ipv6/sockopt_rfc3493_linux.go
new file mode 100644
index 0000000..b2557e8
--- /dev/null
+++ b/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))
+}
diff --git a/ipv6/sockopt_rfc3493_unix.go b/ipv6/sockopt_rfc3493_unix.go
new file mode 100644
index 0000000..b055cf0
--- /dev/null
+++ b/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))
+}
diff --git a/ipv6/sockopt_rfc3493_windows.go b/ipv6/sockopt_rfc3493_windows.go
new file mode 100644
index 0000000..3ffb86d
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/sockopt_rfc3542_bsd.go b/ipv6/sockopt_rfc3542_bsd.go
new file mode 100644
index 0000000..61073b1
--- /dev/null
+++ b/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))
+}
diff --git a/ipv6/sockopt_rfc3542_linux.go b/ipv6/sockopt_rfc3542_linux.go
new file mode 100644
index 0000000..9cea683
--- /dev/null
+++ b/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))
+}
diff --git a/ipv6/sockopt_rfc3542_plan9.go b/ipv6/sockopt_rfc3542_plan9.go
new file mode 100644
index 0000000..53a59f4
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/sockopt_rfc3542_unix.go b/ipv6/sockopt_rfc3542_unix.go
new file mode 100644
index 0000000..463f426
--- /dev/null
+++ b/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)))
+}
diff --git a/ipv6/sockopt_rfc3542_windows.go b/ipv6/sockopt_rfc3542_windows.go
new file mode 100644
index 0000000..2bc22d9
--- /dev/null
+++ b/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
+}
diff --git a/ipv6/sockopt_test.go b/ipv6/sockopt_test.go
new file mode 100644
index 0000000..95b5fad
--- /dev/null
+++ b/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)
+ }
+ }
+}
diff --git a/ipv6/unicast_test.go b/ipv6/unicast_test.go
new file mode 100644
index 0000000..063dc4e
--- /dev/null
+++ b/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)
+ }
+ }
+ }
+}
diff --git a/ipv6/unicastsockopt_test.go b/ipv6/unicastsockopt_test.go
new file mode 100644
index 0000000..a2e5bc5
--- /dev/null
+++ b/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)
+ }
+}