فهرست منبع

go.net/ipv6: restructure ancillary data socket option handling

This CL chops existing ancillary data socket option handlers and
puts them into platform dependent ancillary data socket option
binding table for code readability.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/164580043
Mikio Hara 11 سال پیش
والد
کامیت
4d78cbeaa0
5فایلهای تغییر یافته به همراه268 افزوده شده و 315 حذف شده
  1. 30 128
      ipv6/control_rfc2292_unix.go
  2. 74 187
      ipv6/control_rfc3542_unix.go
  3. 0 0
      ipv6/control_stub.go
  4. 164 0
      ipv6/control_unix.go
  5. 0 0
      ipv6/control_windows.go

+ 30 - 128
ipv6/control_rfc2292_unix.go

@@ -7,145 +7,47 @@
 package ipv6
 
 import (
-	"net"
-	"os"
 	"syscall"
 	"unsafe"
 )
 
-const pktinfo = FlagDst | FlagInterface
-
-func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
-	opt.Lock()
-	defer opt.Unlock()
-	if cf&FlagHopLimit != 0 {
-		if err := setIPv6ReceiveHopLimit(fd, on); err != nil {
-			return err
-		}
-		if on {
-			opt.set(FlagHopLimit)
-		} else {
-			opt.clear(FlagHopLimit)
-		}
-	}
-	if cf&pktinfo != 0 {
-		if err := setIPv6ReceivePacketInfo(fd, on); err != nil {
-			return err
-		}
-		if on {
-			opt.set(cf & pktinfo)
-		} else {
-			opt.clear(cf & pktinfo)
-		}
-	}
-	return nil
-}
-
-func newControlMessage(opt *rawOpt) (oob []byte) {
-	opt.Lock()
-	defer opt.Unlock()
-	l, off := 0, 0
-	if opt.isset(FlagHopLimit) {
-		l += syscall.CmsgSpace(4)
-	}
-	if opt.isset(pktinfo) {
-		l += syscall.CmsgSpace(sysSizeofPacketInfo)
-	}
-	if l > 0 {
-		oob = make([]byte, l)
-		if opt.isset(FlagHopLimit) {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockopt2292HopLimit
-			m.SetLen(syscall.CmsgLen(4))
-			off += syscall.CmsgSpace(4)
-		}
-		if opt.isset(pktinfo) {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockopt2292PacketInfo
-			m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
-			off += syscall.CmsgSpace(sysSizeofPacketInfo)
-		}
+func marshal2292HopLimit(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_2292HOPLIMIT
+	m.SetLen(syscall.CmsgLen(4))
+	if cm != nil {
+		data := b[syscall.CmsgLen(0):]
+		*(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.HopLimit)
 	}
-	return
+	return b[syscall.CmsgSpace(4):]
 }
 
-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
+func marshal2292PacketInfo(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_2292PKTINFO
+	m.SetLen(syscall.CmsgLen(sysSizeofInet6Pktinfo))
+	if cm != nil {
+		pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+		if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+			copy(pi.Addr[:], ip)
 		}
-		switch m.Header.Type {
-		case sysSockopt2292HopLimit:
-			cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
-		case sysSockopt2292PacketInfo:
-			pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
-			cm.IfIndex = int(pi.IfIndex)
-			cm.Dst = pi.IP[:]
+		if cm.IfIndex > 0 {
+			pi.setIfindex(cm.IfIndex)
 		}
 	}
-	return cm, nil
+	return b[syscall.CmsgSpace(sysSizeofInet6Pktinfo):]
 }
 
-func marshalControlMessage(cm *ControlMessage) (oob []byte) {
-	if cm == nil {
-		return
-	}
-	l, off := 0, 0
-	if cm.HopLimit > 0 {
-		l += syscall.CmsgSpace(4)
-	}
-	pion := false
-	if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
-		pion = true
-		l += syscall.CmsgSpace(sysSizeofPacketInfo)
-	}
-	if len(cm.NextHop) == net.IPv6len {
-		l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
-	}
-	if l > 0 {
-		oob = make([]byte, l)
-		if cm.HopLimit > 0 {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockopt2292HopLimit
-			m.SetLen(syscall.CmsgLen(4))
-			data := oob[off+syscall.CmsgLen(0):]
-			*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
-			off += syscall.CmsgSpace(4)
-		}
-		if pion {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockopt2292PacketInfo
-			m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
-			pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
-			if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
-				copy(pi.IP[:], ip)
-			}
-			if cm.IfIndex != 0 {
-				pi.IfIndex = uint32(cm.IfIndex)
-			}
-			off += syscall.CmsgSpace(sysSizeofPacketInfo)
-		}
-		if len(cm.NextHop) == net.IPv6len {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockopt2292NextHop
-			m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
-			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
-			setSockaddr(sa, cm.NextHop, cm.IfIndex)
-			off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
-		}
+func marshal2292NextHop(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_2292NEXTHOP
+	m.SetLen(syscall.CmsgLen(sysSizeofSockaddrInet6))
+	if cm != nil {
+		sa := (*sysSockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+		sa.setSockaddr(cm.NextHop, cm.IfIndex)
 	}
-	return
+	return b[syscall.CmsgSpace(sysSizeofSockaddrInet6):]
 }

+ 74 - 187
ipv6/control_rfc3542_unix.go

@@ -2,209 +2,96 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
 
 package ipv6
 
 import (
-	"net"
-	"os"
 	"syscall"
 	"unsafe"
 )
 
-const pktinfo = FlagDst | FlagInterface
+func marshalTrafficClass(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_TCLASS
+	m.SetLen(syscall.CmsgLen(4))
+	if cm != nil {
+		data := b[syscall.CmsgLen(0):]
+		*(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.TrafficClass)
+	}
+	return b[syscall.CmsgSpace(4):]
+}
 
-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 parseTrafficClass(cm *ControlMessage, b []byte) {
+	cm.TrafficClass = int(*(*int32)(unsafe.Pointer(&b[:4][0])))
 }
 
-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(sysSizeofPacketInfo)
-	}
-	if opt.isset(FlagPathMTU) {
-		l += syscall.CmsgSpace(sysSizeofMTUInfo)
-	}
-	if l > 0 {
-		oob = make([]byte, l)
-		if opt.isset(FlagTrafficClass) {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockoptReceiveTrafficClass
-			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 = sysSockoptReceiveHopLimit
-			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 = sysSockoptReceivePacketInfo
-			m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
-			off += syscall.CmsgSpace(sysSizeofPacketInfo)
-		}
-		if opt.isset(FlagPathMTU) {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockoptReceivePathMTU
-			m.SetLen(syscall.CmsgLen(sysSizeofMTUInfo))
-			off += syscall.CmsgSpace(sysSizeofMTUInfo)
-		}
-	}
-	return
+func marshalHopLimit(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_HOPLIMIT
+	m.SetLen(syscall.CmsgLen(4))
+	if cm != nil {
+		data := b[syscall.CmsgLen(0):]
+		*(*int32)(unsafe.Pointer(&data[:4][0])) = int32(cm.HopLimit)
+	}
+	return b[syscall.CmsgSpace(4):]
 }
 
-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 sysSockoptTrafficClass:
-			cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
-		case sysSockoptHopLimit:
-			cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
-		case sysSockoptPacketInfo:
-			pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
-			cm.Dst = pi.IP[:]
-			cm.IfIndex = int(pi.IfIndex)
-		case sysSockoptPathMTU:
-			mi := (*sysMTUInfo)(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 parseHopLimit(cm *ControlMessage, b []byte) {
+	cm.HopLimit = int(*(*int32)(unsafe.Pointer(&b[:4][0])))
 }
 
-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(sysSizeofPacketInfo)
-	}
-	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 = sysSockoptTrafficClass
-			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 = sysSockoptHopLimit
-			m.SetLen(syscall.CmsgLen(4))
-			data := oob[off+syscall.CmsgLen(0):]
-			*(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
-			off += syscall.CmsgSpace(4)
+func marshalPacketInfo(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_PKTINFO
+	m.SetLen(syscall.CmsgLen(sysSizeofInet6Pktinfo))
+	if cm != nil {
+		pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+		if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
+			copy(pi.Addr[:], ip)
 		}
-		if pion {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockoptPacketInfo
-			m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
-			pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
-			if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
-				copy(pi.IP[:], ip)
-			}
-			if cm.IfIndex != 0 {
-				pi.IfIndex = uint32(cm.IfIndex)
-			}
-			off += syscall.CmsgSpace(sysSizeofPacketInfo)
-		}
-		if len(cm.NextHop) == net.IPv6len {
-			m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
-			m.Level = ianaProtocolIPv6
-			m.Type = sysSockoptNextHop
-			m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
-			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
-			setSockaddr(sa, cm.NextHop, cm.IfIndex)
-			off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
+		if cm.IfIndex > 0 {
+			pi.setIfindex(cm.IfIndex)
 		}
 	}
-	return
+	return b[syscall.CmsgSpace(sysSizeofInet6Pktinfo):]
+}
+
+func parsePacketInfo(cm *ControlMessage, b []byte) {
+	pi := (*sysInet6Pktinfo)(unsafe.Pointer(&b[0]))
+	cm.Dst = pi.Addr[:]
+	cm.IfIndex = int(pi.Ifindex)
+}
+
+func marshalNextHop(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_NEXTHOP
+	m.SetLen(syscall.CmsgLen(sysSizeofSockaddrInet6))
+	if cm != nil {
+		sa := (*sysSockaddrInet6)(unsafe.Pointer(&b[syscall.CmsgLen(0)]))
+		sa.setSockaddr(cm.NextHop, cm.IfIndex)
+	}
+	return b[syscall.CmsgSpace(sysSizeofSockaddrInet6):]
+}
+
+func parseNextHop(cm *ControlMessage, b []byte) {
+}
+
+func marshalPathMTU(b []byte, cm *ControlMessage) []byte {
+	m := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
+	m.Level = ianaProtocolIPv6
+	m.Type = sysIPV6_PATHMTU
+	m.SetLen(syscall.CmsgLen(sysSizeofIPv6Mtuinfo))
+	return b[syscall.CmsgSpace(sysSizeofIPv6Mtuinfo):]
+}
+
+func parsePathMTU(cm *ControlMessage, b []byte) {
+	mi := (*sysIPv6Mtuinfo)(unsafe.Pointer(&b[0]))
+	cm.Dst = mi.Addr.Addr[:]
+	cm.IfIndex = int(mi.Addr.Scope_id)
+	cm.MTU = int(mi.Mtu)
 }

+ 0 - 0
ipv6/control_rfc3542_stub.go → ipv6/control_stub.go


+ 164 - 0
ipv6/control_unix.go

@@ -0,0 +1,164 @@
+// 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 dragonfly freebsd linux netbsd openbsd
+
+package ipv6
+
+import (
+	"os"
+	"syscall"
+)
+
+func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
+	opt.Lock()
+	defer opt.Unlock()
+	if cf&FlagTrafficClass != 0 && sockOpts[ssoReceiveTrafficClass].name > 0 {
+		if err := setInt(fd, &sockOpts[ssoReceiveTrafficClass], boolint(on)); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagTrafficClass)
+		} else {
+			opt.clear(FlagTrafficClass)
+		}
+	}
+	if cf&FlagHopLimit != 0 && sockOpts[ssoReceiveHopLimit].name > 0 {
+		if err := setInt(fd, &sockOpts[ssoReceiveHopLimit], boolint(on)); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagHopLimit)
+		} else {
+			opt.clear(FlagHopLimit)
+		}
+	}
+	if cf&flagPacketInfo != 0 && sockOpts[ssoReceivePacketInfo].name > 0 {
+		if err := setInt(fd, &sockOpts[ssoReceivePacketInfo], boolint(on)); err != nil {
+			return err
+		}
+		if on {
+			opt.set(cf & flagPacketInfo)
+		} else {
+			opt.clear(cf & flagPacketInfo)
+		}
+	}
+	if cf&FlagPathMTU != 0 && sockOpts[ssoReceivePathMTU].name > 0 {
+		if err := setInt(fd, &sockOpts[ssoReceivePathMTU], boolint(on)); err != nil {
+			return err
+		}
+		if on {
+			opt.set(FlagPathMTU)
+		} else {
+			opt.clear(FlagPathMTU)
+		}
+	}
+	return nil
+}
+
+func newControlMessage(opt *rawOpt) (oob []byte) {
+	opt.RLock()
+	var l int
+	if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
+		l += syscall.CmsgSpace(ctlOpts[ctlTrafficClass].length)
+	}
+	if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
+		l += syscall.CmsgSpace(ctlOpts[ctlHopLimit].length)
+	}
+	if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
+		l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
+	}
+	if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
+		l += syscall.CmsgSpace(ctlOpts[ctlPathMTU].length)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		b := oob
+		if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
+			b = ctlOpts[ctlTrafficClass].marshal(b, nil)
+		}
+		if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
+			b = ctlOpts[ctlHopLimit].marshal(b, nil)
+		}
+		if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
+			b = ctlOpts[ctlPacketInfo].marshal(b, nil)
+		}
+		if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
+			b = ctlOpts[ctlPathMTU].marshal(b, nil)
+		}
+	}
+	opt.RUnlock()
+	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 int(m.Header.Type) {
+		case ctlOpts[ctlTrafficClass].name:
+			ctlOpts[ctlTrafficClass].parse(cm, m.Data[:])
+		case ctlOpts[ctlHopLimit].name:
+			ctlOpts[ctlHopLimit].parse(cm, m.Data[:])
+		case ctlOpts[ctlPacketInfo].name:
+			ctlOpts[ctlPacketInfo].parse(cm, m.Data[:])
+		case ctlOpts[ctlPathMTU].name:
+			ctlOpts[ctlPathMTU].parse(cm, m.Data[:])
+		}
+	}
+	return cm, nil
+}
+
+func marshalControlMessage(cm *ControlMessage) (oob []byte) {
+	if cm == nil {
+		return
+	}
+	var l int
+	tclass := false
+	if ctlOpts[ctlTrafficClass].name > 0 && cm.TrafficClass > 0 {
+		tclass = true
+		l += syscall.CmsgSpace(ctlOpts[ctlTrafficClass].length)
+	}
+	hoplimit := false
+	if ctlOpts[ctlHopLimit].name > 0 && cm.HopLimit > 0 {
+		hoplimit = true
+		l += syscall.CmsgSpace(ctlOpts[ctlHopLimit].length)
+	}
+	pktinfo := false
+	if ctlOpts[ctlPacketInfo].name > 0 && cm.Src.To16() != nil && cm.Src.To4() == nil || cm.IfIndex > 0 {
+		pktinfo = true
+		l += syscall.CmsgSpace(ctlOpts[ctlPacketInfo].length)
+	}
+	nexthop := false
+	if ctlOpts[ctlNextHop].name > 0 && cm.NextHop.To16() != nil && cm.NextHop.To4() == nil {
+		nexthop = true
+		l += syscall.CmsgSpace(ctlOpts[ctlNextHop].length)
+	}
+	if l > 0 {
+		oob = make([]byte, l)
+		b := oob
+		if tclass {
+			b = ctlOpts[ctlTrafficClass].marshal(b, cm)
+		}
+		if hoplimit {
+			b = ctlOpts[ctlHopLimit].marshal(b, cm)
+		}
+		if pktinfo {
+			b = ctlOpts[ctlPacketInfo].marshal(b, cm)
+		}
+		if nexthop {
+			b = ctlOpts[ctlNextHop].marshal(b, cm)
+		}
+	}
+	return
+}

+ 0 - 0
ipv6/control_rfc3542_windows.go → ipv6/control_windows.go