control_linux.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // Copyright 2012 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. package ipv4
  5. import (
  6. "os"
  7. "syscall"
  8. "unsafe"
  9. )
  10. // Linux provides a convenient path control option IP_PKTINFO that
  11. // contains IP_SENDSRCADDR, IP_RECVDSTADDR, IP_RECVIF and IP_SENDIF.
  12. const pktinfo = FlagSrc | FlagDst | FlagInterface
  13. func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error {
  14. opt.Lock()
  15. defer opt.Unlock()
  16. if cf&FlagTTL != 0 {
  17. if err := setIPv4ReceiveTTL(fd, on); err != nil {
  18. return err
  19. }
  20. if on {
  21. opt.set(FlagTTL)
  22. } else {
  23. opt.clear(FlagTTL)
  24. }
  25. }
  26. if cf&pktinfo != 0 {
  27. if err := setIPv4PacketInfo(fd, on); err != nil {
  28. return err
  29. }
  30. if on {
  31. opt.set(cf & pktinfo)
  32. } else {
  33. opt.clear(cf & pktinfo)
  34. }
  35. }
  36. return nil
  37. }
  38. func newControlMessage(opt *rawOpt) (oob []byte) {
  39. opt.Lock()
  40. defer opt.Unlock()
  41. l, off := 0, 0
  42. if opt.isset(FlagTTL) {
  43. l += syscall.CmsgSpace(1)
  44. }
  45. if opt.isset(pktinfo) {
  46. l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
  47. }
  48. if l > 0 {
  49. oob = make([]byte, l)
  50. if opt.isset(FlagTTL) {
  51. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  52. m.Level = ianaProtocolIP
  53. m.Type = syscall.IP_RECVTTL
  54. m.SetLen(syscall.CmsgLen(1))
  55. off += syscall.CmsgSpace(1)
  56. }
  57. if opt.isset(pktinfo) {
  58. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  59. m.Level = ianaProtocolIP
  60. m.Type = syscall.IP_PKTINFO
  61. m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
  62. off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
  63. }
  64. }
  65. return
  66. }
  67. func parseControlMessage(b []byte) (*ControlMessage, error) {
  68. if len(b) == 0 {
  69. return nil, nil
  70. }
  71. cmsgs, err := syscall.ParseSocketControlMessage(b)
  72. if err != nil {
  73. return nil, os.NewSyscallError("parse socket control message", err)
  74. }
  75. cm := &ControlMessage{}
  76. for _, m := range cmsgs {
  77. if m.Header.Level != ianaProtocolIP {
  78. continue
  79. }
  80. switch m.Header.Type {
  81. case syscall.IP_TTL:
  82. cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0])))
  83. case syscall.IP_PKTINFO:
  84. pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0]))
  85. cm.IfIndex = int(pi.Ifindex)
  86. cm.Dst = pi.Addr[:]
  87. }
  88. }
  89. return cm, nil
  90. }
  91. func marshalControlMessage(cm *ControlMessage) (oob []byte) {
  92. if cm == nil {
  93. return
  94. }
  95. l, off := 0, 0
  96. pion := false
  97. if cm.Src.To4() != nil || cm.IfIndex != 0 {
  98. pion = true
  99. l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
  100. }
  101. if l > 0 {
  102. oob = make([]byte, l)
  103. if pion {
  104. m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off]))
  105. m.Level = ianaProtocolIP
  106. m.Type = syscall.IP_PKTINFO
  107. m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo))
  108. pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)]))
  109. if ip := cm.Src.To4(); ip != nil {
  110. copy(pi.Addr[:], ip)
  111. }
  112. if cm.IfIndex != 0 {
  113. pi.Ifindex = int32(cm.IfIndex)
  114. }
  115. off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo)
  116. }
  117. }
  118. return
  119. }