icmp_windows_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. //go:build windows
  2. package ingress
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "fmt"
  7. "io"
  8. "net/netip"
  9. "testing"
  10. "time"
  11. "unsafe"
  12. "golang.org/x/net/icmp"
  13. "github.com/stretchr/testify/require"
  14. )
  15. // TestParseEchoReply tests parsing raw bytes from icmpSendEcho into echoResp
  16. func TestParseEchoReply(t *testing.T) {
  17. dst, err := inAddrV4(netip.MustParseAddr("192.168.10.20"))
  18. require.NoError(t, err)
  19. validReplyData := []byte(t.Name())
  20. validReply := echoReply{
  21. Address: dst,
  22. Status: success,
  23. RoundTripTime: uint32(20),
  24. DataSize: uint16(len(validReplyData)),
  25. DataPointer: &validReplyData[0],
  26. Options: ipOption{
  27. TTL: 59,
  28. },
  29. }
  30. destHostUnreachableReply := validReply
  31. destHostUnreachableReply.Status = destHostUnreachable
  32. tests := []struct {
  33. testCase string
  34. replyBuf []byte
  35. expectedReply *echoReply
  36. expectedData []byte
  37. }{
  38. {
  39. testCase: "empty buffer",
  40. },
  41. {
  42. testCase: "status not success",
  43. replyBuf: destHostUnreachableReply.marshal(t, []byte{}),
  44. },
  45. {
  46. testCase: "valid reply",
  47. replyBuf: validReply.marshal(t, validReplyData),
  48. expectedReply: &validReply,
  49. expectedData: validReplyData,
  50. },
  51. }
  52. for _, test := range tests {
  53. resp, err := newEchoV4Resp(test.replyBuf)
  54. if test.expectedReply == nil {
  55. require.Error(t, err)
  56. require.Nil(t, resp)
  57. } else {
  58. require.NoError(t, err)
  59. require.Equal(t, resp.reply, test.expectedReply)
  60. require.True(t, bytes.Equal(resp.data, test.expectedData))
  61. }
  62. }
  63. }
  64. // TestParseEchoV6Reply tests parsing raw bytes from icmp6SendEcho into echoV6Resp
  65. func TestParseEchoV6Reply(t *testing.T) {
  66. dst := netip.MustParseAddr("2606:3600:4500::3333").As16()
  67. var addr [8]uint16
  68. for i := 0; i < 8; i++ {
  69. addr[i] = binary.BigEndian.Uint16(dst[i*2 : i*2+2])
  70. }
  71. validReplyData := []byte(t.Name())
  72. validReply := echoV6Reply{
  73. Address: ipv6AddrEx{
  74. addr: addr,
  75. },
  76. Status: success,
  77. RoundTripTime: 25,
  78. }
  79. destHostUnreachableReply := validReply
  80. destHostUnreachableReply.Status = ipv6DestUnreachable
  81. tests := []struct {
  82. testCase string
  83. replyBuf []byte
  84. expectedReply *echoV6Reply
  85. expectedData []byte
  86. }{
  87. {
  88. testCase: "empty buffer",
  89. },
  90. {
  91. testCase: "status not success",
  92. replyBuf: destHostUnreachableReply.marshal(t, []byte{}),
  93. },
  94. {
  95. testCase: "valid reply",
  96. replyBuf: validReply.marshal(t, validReplyData),
  97. expectedReply: &validReply,
  98. expectedData: validReplyData,
  99. },
  100. }
  101. for _, test := range tests {
  102. resp, err := newEchoV6Resp(test.replyBuf, len(test.expectedData))
  103. if test.expectedReply == nil {
  104. require.Error(t, err)
  105. require.Nil(t, resp)
  106. } else {
  107. require.NoError(t, err)
  108. require.Equal(t, resp.reply, test.expectedReply)
  109. require.True(t, bytes.Equal(resp.data, test.expectedData))
  110. }
  111. }
  112. }
  113. // TestSendEchoErrors makes sure icmpSendEcho handles error cases
  114. func TestSendEchoErrors(t *testing.T) {
  115. testSendEchoErrors(t, netip.IPv4Unspecified())
  116. testSendEchoErrors(t, netip.IPv6Unspecified())
  117. }
  118. func testSendEchoErrors(t *testing.T, listenIP netip.Addr) {
  119. proxy, err := newICMPProxy(listenIP, &noopLogger, time.Second)
  120. require.NoError(t, err)
  121. echo := icmp.Echo{
  122. ID: 6193,
  123. Seq: 25712,
  124. Data: []byte(t.Name()),
  125. }
  126. documentIP := netip.MustParseAddr("192.0.2.200")
  127. if listenIP.Is6() {
  128. documentIP = netip.MustParseAddr("2001:db8::1")
  129. }
  130. resp, err := proxy.icmpEchoRoundtrip(documentIP, &echo)
  131. require.Error(t, err)
  132. require.Nil(t, resp)
  133. }
  134. func (er *echoReply) marshal(t *testing.T, data []byte) []byte {
  135. buf := new(bytes.Buffer)
  136. for _, field := range []any{
  137. er.Address,
  138. er.Status,
  139. er.RoundTripTime,
  140. er.DataSize,
  141. er.Reserved,
  142. } {
  143. require.NoError(t, binary.Write(buf, endian, field))
  144. }
  145. require.NoError(t, marshalPointer(buf, uintptr(unsafe.Pointer(er.DataPointer))))
  146. for _, field := range []any{
  147. er.Options.TTL,
  148. er.Options.Tos,
  149. er.Options.Flags,
  150. er.Options.OptionsSize,
  151. } {
  152. require.NoError(t, binary.Write(buf, endian, field))
  153. }
  154. require.NoError(t, marshalPointer(buf, er.Options.OptionsData))
  155. padSize := buf.Len() % int(unsafe.Alignof(er))
  156. padding := make([]byte, padSize)
  157. n, err := buf.Write(padding)
  158. require.NoError(t, err)
  159. require.Equal(t, padSize, n)
  160. n, err = buf.Write(data)
  161. require.NoError(t, err)
  162. require.Equal(t, len(data), n)
  163. return buf.Bytes()
  164. }
  165. func marshalPointer(buf io.Writer, ptr uintptr) error {
  166. size := unsafe.Sizeof(ptr)
  167. switch size {
  168. case 4:
  169. return binary.Write(buf, endian, uint32(ptr))
  170. case 8:
  171. return binary.Write(buf, endian, uint64(ptr))
  172. default:
  173. return fmt.Errorf("unexpected pointer size %d", size)
  174. }
  175. }
  176. func (er *echoV6Reply) marshal(t *testing.T, data []byte) []byte {
  177. buf := new(bytes.Buffer)
  178. for _, field := range []any{
  179. er.Address.port,
  180. er.Address.flowInfoUpper,
  181. er.Address.flowInfoLower,
  182. er.Address.addr,
  183. er.Address.scopeID,
  184. } {
  185. require.NoError(t, binary.Write(buf, endian, field))
  186. }
  187. padSize := buf.Len() % int(unsafe.Alignof(er))
  188. padding := make([]byte, padSize)
  189. n, err := buf.Write(padding)
  190. require.NoError(t, err)
  191. require.Equal(t, padSize, n)
  192. for _, field := range []any{
  193. er.Status,
  194. er.RoundTripTime,
  195. } {
  196. require.NoError(t, binary.Write(buf, endian, field))
  197. }
  198. n, err = buf.Write(data)
  199. require.NoError(t, err)
  200. require.Equal(t, len(data), n)
  201. return buf.Bytes()
  202. }