mockicmp_test.go 2.9 KB

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