mockicmp_test.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // Copyright 2013 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 ipv6_test
  5. import (
  6. "code.google.com/p/go.net/ipv6"
  7. "errors"
  8. )
  9. // icmpMessage represents an ICMP message.
  10. type icmpMessage struct {
  11. Type ipv6.ICMPType // type
  12. Code int // code
  13. Checksum int // checksum
  14. Body icmpMessageBody // body
  15. }
  16. // icmpMessageBody represents an ICMP message body.
  17. type icmpMessageBody interface {
  18. Len() int
  19. Marshal() ([]byte, error)
  20. }
  21. // Marshal returns the binary enconding of the ICMP echo request or
  22. // reply message m.
  23. func (m *icmpMessage) Marshal() ([]byte, error) {
  24. b := []byte{byte(m.Type), byte(m.Code), 0, 0}
  25. if m.Body != nil && m.Body.Len() != 0 {
  26. mb, err := m.Body.Marshal()
  27. if err != nil {
  28. return nil, err
  29. }
  30. b = append(b, mb...)
  31. }
  32. switch m.Type {
  33. case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
  34. return b, nil
  35. }
  36. csumcv := len(b) - 1 // checksum coverage
  37. s := uint32(0)
  38. for i := 0; i < csumcv; i += 2 {
  39. s += uint32(b[i+1])<<8 | uint32(b[i])
  40. }
  41. if csumcv&1 == 0 {
  42. s += uint32(b[csumcv])
  43. }
  44. s = s>>16 + s&0xffff
  45. s = s + s>>16
  46. // Place checksum back in header; using ^= avoids the
  47. // assumption the checksum bytes are zero.
  48. b[2] ^= byte(^s)
  49. b[3] ^= byte(^s >> 8)
  50. return b, nil
  51. }
  52. // parseICMPMessage parses b as an ICMP message.
  53. func parseICMPMessage(b []byte) (*icmpMessage, error) {
  54. msglen := len(b)
  55. if msglen < 4 {
  56. return nil, errors.New("message too short")
  57. }
  58. m := &icmpMessage{Type: ipv6.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
  59. if msglen > 4 {
  60. var err error
  61. switch m.Type {
  62. case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply:
  63. m.Body, err = parseICMPEcho(b[4:])
  64. if err != nil {
  65. return nil, err
  66. }
  67. }
  68. }
  69. return m, nil
  70. }
  71. // imcpEcho represenets an ICMP echo request or reply message body.
  72. type icmpEcho struct {
  73. ID int // identifier
  74. Seq int // sequence number
  75. Data []byte // data
  76. }
  77. func (p *icmpEcho) Len() int {
  78. if p == nil {
  79. return 0
  80. }
  81. return 4 + len(p.Data)
  82. }
  83. // Marshal returns the binary enconding of the ICMP echo request or
  84. // reply message body p.
  85. func (p *icmpEcho) Marshal() ([]byte, error) {
  86. b := make([]byte, 4+len(p.Data))
  87. b[0], b[1] = byte(p.ID>>8), byte(p.ID)
  88. b[2], b[3] = byte(p.Seq>>8), byte(p.Seq)
  89. copy(b[4:], p.Data)
  90. return b, nil
  91. }
  92. // parseICMPEcho parses b as an ICMP echo request or reply message
  93. // body.
  94. func parseICMPEcho(b []byte) (*icmpEcho, error) {
  95. bodylen := len(b)
  96. p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
  97. if bodylen > 4 {
  98. p.Data = make([]byte, bodylen-4)
  99. copy(p.Data, b[4:])
  100. }
  101. return p, nil
  102. }