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.

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