message.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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 icmp provides basic functions for the manipulation of ICMP
  5. // message.
  6. package icmp
  7. import (
  8. "errors"
  9. "net"
  10. "syscall"
  11. "golang.org/x/net/internal/iana"
  12. "golang.org/x/net/ipv4"
  13. "golang.org/x/net/ipv6"
  14. )
  15. var (
  16. errMessageTooShort = errors.New("message too short")
  17. errHeaderTooShort = errors.New("header too short")
  18. errBufferTooShort = errors.New("buffer too short")
  19. errOpNoSupport = errors.New("operation not supported")
  20. )
  21. // A Type represents an ICMP message type.
  22. type Type interface {
  23. Protocol() int
  24. }
  25. // A Message represents an ICMP message.
  26. type Message struct {
  27. Type Type // type, either ipv4.ICMPType or ipv6.ICMPType
  28. Code int // code
  29. Checksum int // checksum
  30. Body MessageBody // body
  31. }
  32. // Marshal returns the binary enconding of the ICMP message m.
  33. //
  34. // For ICMP for IPv4 message, the returned message always contains the
  35. // calculated checksum field.
  36. //
  37. // For ICMP for IPv6 message, the returned message contains the
  38. // calculated checksum field when psh is not nil, otherwise the kernel
  39. // will compute the checksum field during the message transmission.
  40. // When psh is not nil, it must be the pseudo header for IPv6.
  41. func (m *Message) Marshal(psh []byte) ([]byte, error) {
  42. var mtype int
  43. switch typ := m.Type.(type) {
  44. case ipv4.ICMPType:
  45. mtype = int(typ)
  46. case ipv6.ICMPType:
  47. mtype = int(typ)
  48. default:
  49. return nil, syscall.EINVAL
  50. }
  51. b := []byte{byte(mtype), byte(m.Code), 0, 0}
  52. if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil {
  53. b = append(psh, b...)
  54. }
  55. if m.Body != nil && m.Body.Len() != 0 {
  56. mb, err := m.Body.Marshal()
  57. if err != nil {
  58. return nil, err
  59. }
  60. b = append(b, mb...)
  61. }
  62. if m.Type.Protocol() == iana.ProtocolIPv6ICMP {
  63. if psh == nil { // cannot calculate checksum here
  64. return b, nil
  65. }
  66. off, l := 2*net.IPv6len, len(b)-len(psh)
  67. b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
  68. }
  69. csumcv := len(b) - 1 // checksum coverage
  70. s := uint32(0)
  71. for i := 0; i < csumcv; i += 2 {
  72. s += uint32(b[i+1])<<8 | uint32(b[i])
  73. }
  74. if csumcv&1 == 0 {
  75. s += uint32(b[csumcv])
  76. }
  77. s = s>>16 + s&0xffff
  78. s = s + s>>16
  79. // Place checksum back in header; using ^= avoids the
  80. // assumption the checksum bytes are zero.
  81. b[len(psh)+2] ^= byte(^s)
  82. b[len(psh)+3] ^= byte(^s >> 8)
  83. return b[len(psh):], nil
  84. }
  85. var parseFns = map[Type]func([]byte) (MessageBody, error){
  86. ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
  87. ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
  88. ipv4.ICMPTypeParameterProblem: parseParamProb,
  89. ipv4.ICMPTypeEcho: parseEcho,
  90. ipv4.ICMPTypeEchoReply: parseEcho,
  91. ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
  92. ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
  93. ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
  94. ipv6.ICMPTypeParameterProblem: parseParamProb,
  95. ipv6.ICMPTypeEchoRequest: parseEcho,
  96. ipv6.ICMPTypeEchoReply: parseEcho,
  97. }
  98. // ParseMessage parses b as an ICMP message.
  99. // Proto must be either the ICMPv4 or ICMPv6 protocol number.
  100. func ParseMessage(proto int, b []byte) (*Message, error) {
  101. if len(b) < 4 {
  102. return nil, errMessageTooShort
  103. }
  104. var err error
  105. m := &Message{Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
  106. switch proto {
  107. case iana.ProtocolICMP:
  108. m.Type = ipv4.ICMPType(b[0])
  109. case iana.ProtocolIPv6ICMP:
  110. m.Type = ipv6.ICMPType(b[0])
  111. default:
  112. return nil, syscall.EINVAL
  113. }
  114. if fn, ok := parseFns[m.Type]; !ok {
  115. m.Body, err = parseDefaultMessageBody(b[4:])
  116. } else {
  117. m.Body, err = fn(b[4:])
  118. }
  119. if err != nil {
  120. return nil, err
  121. }
  122. return m, nil
  123. }