message.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. "code.google.com/p/go.net/internal/iana"
  11. "code.google.com/p/go.net/ipv4"
  12. "code.google.com/p/go.net/ipv6"
  13. )
  14. // A Type represents an ICMP message type.
  15. type Type interface {
  16. String() string
  17. }
  18. // A Message represents an ICMP message.
  19. type Message struct {
  20. Type Type // type, either ipv4.ICMPType or ipv6.ICMPType
  21. Code int // code
  22. Checksum int // checksum
  23. Body MessageBody // body
  24. }
  25. // Marshal returns the binary enconding of the ICMP message m.
  26. //
  27. // For ICMP for IPv4 message, the returned message always contains the
  28. // calculated checksum field.
  29. //
  30. // For ICMP for IPv6 message, the returned message contains the
  31. // calculated checksum field when psh is not nil, otherwise the kernel
  32. // will compute the checksum field during the message transmission.
  33. // When psh is not nil, it must be the pseudo header for IPv6.
  34. func (m *Message) Marshal(psh []byte) ([]byte, error) {
  35. var mtype int
  36. var icmpv6 bool
  37. switch typ := m.Type.(type) {
  38. case ipv4.ICMPType:
  39. mtype = int(typ)
  40. case ipv6.ICMPType:
  41. mtype = int(typ)
  42. icmpv6 = true
  43. default:
  44. return nil, errors.New("invalid argument")
  45. }
  46. b := []byte{byte(mtype), byte(m.Code), 0, 0}
  47. if icmpv6 && psh != nil {
  48. b = append(psh, b...)
  49. }
  50. if m.Body != nil && m.Body.Len() != 0 {
  51. mb, err := m.Body.Marshal()
  52. if err != nil {
  53. return nil, err
  54. }
  55. b = append(b, mb...)
  56. }
  57. if icmpv6 {
  58. if psh == nil { // cannot calculate checkshum here
  59. return b, nil
  60. }
  61. off, l := 2*net.IPv6len, len(b)-len(psh)
  62. b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
  63. }
  64. csumcv := len(b) - 1 // checksum coverage
  65. s := uint32(0)
  66. for i := 0; i < csumcv; i += 2 {
  67. s += uint32(b[i+1])<<8 | uint32(b[i])
  68. }
  69. if csumcv&1 == 0 {
  70. s += uint32(b[csumcv])
  71. }
  72. s = s>>16 + s&0xffff
  73. s = s + s>>16
  74. // Place checksum back in header; using ^= avoids the
  75. // assumption the checksum bytes are zero.
  76. b[len(psh)+2] ^= byte(^s)
  77. b[len(psh)+3] ^= byte(^s >> 8)
  78. return b[len(psh):], nil
  79. }
  80. // ParseMessage parses b as an ICMP message. Proto must be
  81. // iana.ProtocolICMP or iana.ProtocolIPv6ICMP.
  82. func ParseMessage(proto int, b []byte) (*Message, error) {
  83. if len(b) < 4 {
  84. return nil, errors.New("message too short")
  85. }
  86. var err error
  87. switch proto {
  88. case iana.ProtocolICMP:
  89. m := &Message{Type: ipv4.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
  90. switch m.Type {
  91. case ipv4.ICMPTypeEcho, ipv4.ICMPTypeEchoReply:
  92. m.Body, err = parseEcho(b[4:])
  93. if err != nil {
  94. return nil, err
  95. }
  96. default:
  97. m.Body = &DefaultMessageBody{Data: b[4:]}
  98. }
  99. return m, nil
  100. case iana.ProtocolIPv6ICMP:
  101. m := &Message{Type: ipv6.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
  102. switch m.Type {
  103. case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
  104. m.Body, err = parseEcho(b[4:])
  105. if err != nil {
  106. return nil, err
  107. }
  108. default:
  109. m.Body = &DefaultMessageBody{Data: b[4:]}
  110. }
  111. return m, nil
  112. default:
  113. return nil, errors.New("unknown protocol")
  114. }
  115. }