control_linux.go 2.9 KB

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