You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

164 lines
4.9 KiB

  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
  5. // messages used in the Internet Control Message Protocols,
  6. // ICMPv4 and ICMPv6.
  7. //
  8. // ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
  9. // Multi-part message support for ICMP is defined in RFC 4884.
  10. // ICMP extensions for MPLS are defined in RFC 4950.
  11. // ICMP extensions for interface and next-hop identification are
  12. // defined in RFC 5837.
  13. // PROBE: A utility for probing interfaces is defined in RFC 8335.
  14. package icmp // import "golang.org/x/net/icmp"
  15. import (
  16. "encoding/binary"
  17. "errors"
  18. "net"
  19. "runtime"
  20. "golang.org/x/net/internal/iana"
  21. "golang.org/x/net/ipv4"
  22. "golang.org/x/net/ipv6"
  23. )
  24. // BUG(mikio): This package is not implemented on AIX, JS, NaCl and
  25. // Plan 9.
  26. var (
  27. errInvalidConn = errors.New("invalid connection")
  28. errInvalidProtocol = errors.New("invalid protocol")
  29. errMessageTooShort = errors.New("message too short")
  30. errHeaderTooShort = errors.New("header too short")
  31. errBufferTooShort = errors.New("buffer too short")
  32. errInvalidBody = errors.New("invalid body")
  33. errNoExtension = errors.New("no extension")
  34. errInvalidExtension = errors.New("invalid extension")
  35. errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
  36. )
  37. func checksum(b []byte) uint16 {
  38. csumcv := len(b) - 1 // checksum coverage
  39. s := uint32(0)
  40. for i := 0; i < csumcv; i += 2 {
  41. s += uint32(b[i+1])<<8 | uint32(b[i])
  42. }
  43. if csumcv&1 == 0 {
  44. s += uint32(b[csumcv])
  45. }
  46. s = s>>16 + s&0xffff
  47. s = s + s>>16
  48. return ^uint16(s)
  49. }
  50. // A Type represents an ICMP message type.
  51. type Type interface {
  52. Protocol() int
  53. }
  54. // A Message represents an ICMP message.
  55. type Message struct {
  56. Type Type // type, either ipv4.ICMPType or ipv6.ICMPType
  57. Code int // code
  58. Checksum int // checksum
  59. Body MessageBody // body
  60. }
  61. // Marshal returns the binary encoding of the ICMP message m.
  62. //
  63. // For an ICMPv4 message, the returned message always contains the
  64. // calculated checksum field.
  65. //
  66. // For an ICMPv6 message, the returned message contains the calculated
  67. // checksum field when psh is not nil, otherwise the kernel will
  68. // compute the checksum field during the message transmission.
  69. // When psh is not nil, it must be the pseudo header for IPv6.
  70. func (m *Message) Marshal(psh []byte) ([]byte, error) {
  71. var mtype byte
  72. switch typ := m.Type.(type) {
  73. case ipv4.ICMPType:
  74. mtype = byte(typ)
  75. case ipv6.ICMPType:
  76. mtype = byte(typ)
  77. default:
  78. return nil, errInvalidProtocol
  79. }
  80. b := []byte{mtype, byte(m.Code), 0, 0}
  81. proto := m.Type.Protocol()
  82. if proto == iana.ProtocolIPv6ICMP && psh != nil {
  83. b = append(psh, b...)
  84. }
  85. if m.Body != nil && m.Body.Len(proto) != 0 {
  86. mb, err := m.Body.Marshal(proto)
  87. if err != nil {
  88. return nil, err
  89. }
  90. b = append(b, mb...)
  91. }
  92. if proto == iana.ProtocolIPv6ICMP {
  93. if psh == nil { // cannot calculate checksum here
  94. return b, nil
  95. }
  96. off, l := 2*net.IPv6len, len(b)-len(psh)
  97. binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
  98. }
  99. s := checksum(b)
  100. // Place checksum back in header; using ^= avoids the
  101. // assumption the checksum bytes are zero.
  102. b[len(psh)+2] ^= byte(s)
  103. b[len(psh)+3] ^= byte(s >> 8)
  104. return b[len(psh):], nil
  105. }
  106. var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
  107. ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
  108. ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
  109. ipv4.ICMPTypeParameterProblem: parseParamProb,
  110. ipv4.ICMPTypeEcho: parseEcho,
  111. ipv4.ICMPTypeEchoReply: parseEcho,
  112. ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
  113. ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
  114. ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
  115. ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
  116. ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
  117. ipv6.ICMPTypeParameterProblem: parseParamProb,
  118. ipv6.ICMPTypeEchoRequest: parseEcho,
  119. ipv6.ICMPTypeEchoReply: parseEcho,
  120. ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
  121. ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
  122. }
  123. // ParseMessage parses b as an ICMP message.
  124. // The provided proto must be either the ICMPv4 or ICMPv6 protocol
  125. // number.
  126. func ParseMessage(proto int, b []byte) (*Message, error) {
  127. if len(b) < 4 {
  128. return nil, errMessageTooShort
  129. }
  130. var err error
  131. m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))}
  132. switch proto {
  133. case iana.ProtocolICMP:
  134. m.Type = ipv4.ICMPType(b[0])
  135. case iana.ProtocolIPv6ICMP:
  136. m.Type = ipv6.ICMPType(b[0])
  137. default:
  138. return nil, errInvalidProtocol
  139. }
  140. if fn, ok := parseFns[m.Type]; !ok {
  141. m.Body, err = parseRawBody(proto, b[4:])
  142. } else {
  143. m.Body, err = fn(proto, m.Type, b[4:])
  144. }
  145. if err != nil {
  146. return nil, err
  147. }
  148. return m, nil
  149. }