control_rfc3542_unix.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build freebsd linux netbsd openbsd
  5. package ipv6
  6. import (
  7. "net"
  8. "os"
  9. "syscall"
  10. "unsafe"
  11. )
  12. const pktinfo = FlagDst | FlagInterface
  13. func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
  14. opt.Lock()
  15. defer opt.Unlock()
  16. if cf&FlagTrafficClass != 0 {
  17. if err := setIPv6ReceiveTrafficClass(fd, on); err != nil {
  18. return err
  19. }
  20. if on {
  21. opt.set(FlagTrafficClass)
  22. } else {
  23. opt.clear(FlagTrafficClass)
  24. }
  25. }
  26. if cf&FlagHopLimit != 0 {
  27. if err := setIPv6ReceiveHopLimit(fd, on); err != nil {
  28. return err
  29. }
  30. if on {
  31. opt.set(FlagHopLimit)
  32. } else {
  33. opt.clear(FlagHopLimit)
  34. }
  35. }
  36. if cf&pktinfo != 0 {
  37. if err := setIPv6ReceivePacketInfo(fd, on); err != nil {
  38. return err
  39. }
  40. if on {
  41. opt.set(cf & pktinfo)
  42. } else {
  43. opt.clear(cf & pktinfo)
  44. }
  45. }
  46. if cf&FlagPathMTU != 0 {
  47. if err := setIPv6ReceivePathMTU(fd, on); err != nil {
  48. return err
  49. }
  50. if on {
  51. opt.set(FlagPathMTU)
  52. } else {
  53. opt.clear(FlagPathMTU)
  54. }
  55. }
  56. return nil
  57. }
  58. func newControlMessage(opt *rawOpt) (oob []byte) {
  59. opt.Lock()
  60. defer opt.Unlock()
  61. l, off := 0, 0
  62. if opt.isset(FlagTrafficClass) {
  63. l += syscall.CmsgSpace(4)
  64. }
  65. if opt.isset(FlagHopLimit) {
  66. l += syscall.CmsgSpace(4)
  67. }
  68. if opt.isset(pktinfo) {
  69. l += syscall.CmsgSpace(sysSizeofPacketInfo)
  70. }
  71. if opt.isset(FlagPathMTU) {
  72. l += syscall.CmsgSpace(sysSizeofMTUInfo)
  73. }
  74. if l > 0 {
  75. oob = make([]byte, l)
  76. if opt.isset(FlagTrafficClass) {
  77. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  78. m.Level = ianaProtocolIPv6
  79. m.Type = sysSockoptReceiveTrafficClass
  80. m.SetLen(syscall.CmsgLen(4))
  81. off += syscall.CmsgSpace(4)
  82. }
  83. if opt.isset(FlagHopLimit) {
  84. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  85. m.Level = ianaProtocolIPv6
  86. m.Type = sysSockoptReceiveHopLimit
  87. m.SetLen(syscall.CmsgLen(4))
  88. off += syscall.CmsgSpace(4)
  89. }
  90. if opt.isset(pktinfo) {
  91. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  92. m.Level = ianaProtocolIPv6
  93. m.Type = sysSockoptReceivePacketInfo
  94. m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
  95. off += syscall.CmsgSpace(sysSizeofPacketInfo)
  96. }
  97. if opt.isset(FlagPathMTU) {
  98. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  99. m.Level = ianaProtocolIPv6
  100. m.Type = sysSockoptReceivePathMTU
  101. m.SetLen(syscall.CmsgLen(sysSizeofMTUInfo))
  102. off += syscall.CmsgSpace(sysSizeofMTUInfo)
  103. }
  104. }
  105. return
  106. }
  107. func parseControlMessage(b []byte) (*ControlMessage, error) {
  108. if len(b) == 0 {
  109. return nil, nil
  110. }
  111. cmsgs, err := syscall.ParseSocketControlMessage(b)
  112. if err != nil {
  113. return nil, os.NewSyscallError("parse socket control message", err)
  114. }
  115. cm := &ControlMessage{}
  116. for _, m := range cmsgs {
  117. if m.Header.Level != ianaProtocolIPv6 {
  118. continue
  119. }
  120. switch m.Header.Type {
  121. case sysSockoptTrafficClass:
  122. cm.TrafficClass = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
  123. case sysSockoptHopLimit:
  124. cm.HopLimit = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
  125. case sysSockoptPacketInfo:
  126. pi := (*sysPacketInfo)(unsafe.Pointer(&m.Data[0]))
  127. cm.Dst = pi.IP[:]
  128. cm.IfIndex = int(pi.IfIndex)
  129. case sysSockoptPathMTU:
  130. mi := (*sysMTUInfo)(unsafe.Pointer(&m.Data[0]))
  131. cm.Dst = mi.Addr.Addr[:]
  132. cm.IfIndex = int(mi.Addr.Scope_id)
  133. cm.MTU = int(mi.MTU)
  134. }
  135. }
  136. return cm, nil
  137. }
  138. func marshalControlMessage(cm *ControlMessage) (oob []byte) {
  139. if cm == nil {
  140. return
  141. }
  142. l, off := 0, 0
  143. if cm.TrafficClass > 0 {
  144. l += syscall.CmsgSpace(4)
  145. }
  146. if cm.HopLimit > 0 {
  147. l += syscall.CmsgSpace(4)
  148. }
  149. pion := false
  150. if cm.Src.To4() == nil && cm.Src.To16() != nil || cm.IfIndex != 0 {
  151. pion = true
  152. l += syscall.CmsgSpace(sysSizeofPacketInfo)
  153. }
  154. if len(cm.NextHop) == net.IPv6len {
  155. l += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
  156. }
  157. if l > 0 {
  158. oob = make([]byte, l)
  159. if cm.TrafficClass > 0 {
  160. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  161. m.Level = ianaProtocolIPv6
  162. m.Type = sysSockoptTrafficClass
  163. m.SetLen(syscall.CmsgLen(4))
  164. data := oob[off+syscall.CmsgLen(0):]
  165. *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.TrafficClass)
  166. off += syscall.CmsgSpace(4)
  167. }
  168. if cm.HopLimit > 0 {
  169. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  170. m.Level = ianaProtocolIPv6
  171. m.Type = sysSockoptHopLimit
  172. m.SetLen(syscall.CmsgLen(4))
  173. data := oob[off+syscall.CmsgLen(0):]
  174. *(*byte)(unsafe.Pointer(&data[:1][0])) = byte(cm.HopLimit)
  175. off += syscall.CmsgSpace(4)
  176. }
  177. if pion {
  178. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  179. m.Level = ianaProtocolIPv6
  180. m.Type = sysSockoptPacketInfo
  181. m.SetLen(syscall.CmsgLen(sysSizeofPacketInfo))
  182. pi := (*sysPacketInfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
  183. if ip := cm.Src.To16(); ip != nil && ip.To4() == nil {
  184. copy(pi.IP[:], ip)
  185. }
  186. if cm.IfIndex != 0 {
  187. pi.IfIndex = uint32(cm.IfIndex)
  188. }
  189. off += syscall.CmsgSpace(sysSizeofPacketInfo)
  190. }
  191. if len(cm.NextHop) == net.IPv6len {
  192. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  193. m.Level = ianaProtocolIPv6
  194. m.Type = sysSockoptNextHop
  195. m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrInet6))
  196. sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
  197. setSockaddr(sa, cm.NextHop, cm.IfIndex)
  198. off += syscall.CmsgSpace(syscall.SizeofSockaddrInet6)
  199. }
  200. }
  201. return
  202. }