message.go 4.1 KB

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