compression_test.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package aerotun
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "testing"
  6. )
  7. func buildMockIPv4Packet(proto byte, srcPort, dstPort uint16, payloadLen int) []byte {
  8. hdrLen := 20
  9. pkt := make([]byte, hdrLen+4+payloadLen)
  10. pkt[0] = 0x45 // IPv4, IHL=5 (20 bytes)
  11. binary.BigEndian.PutUint16(pkt[2:4], uint16(len(pkt)))
  12. pkt[9] = proto
  13. copy(pkt[12:16], []byte{10, 99, 0, 1})
  14. copy(pkt[16:20], []byte{10, 99, 0, 2})
  15. if proto == 6 || proto == 17 {
  16. binary.BigEndian.PutUint16(pkt[20:22], srcPort)
  17. binary.BigEndian.PutUint16(pkt[22:24], dstPort)
  18. }
  19. return pkt
  20. }
  21. func buildMockIPv6Packet(proto byte, srcPort, dstPort uint16, payloadLen int) []byte {
  22. pkt := make([]byte, 40+4+payloadLen)
  23. pkt[0] = 0x60 // IPv6
  24. binary.BigEndian.PutUint16(pkt[4:6], uint16(4+payloadLen))
  25. pkt[6] = proto
  26. if proto == 6 || proto == 17 {
  27. binary.BigEndian.PutUint16(pkt[40:42], srcPort)
  28. binary.BigEndian.PutUint16(pkt[42:44], dstPort)
  29. }
  30. return pkt
  31. }
  32. func TestIsEncryptedIPPacket(t *testing.T) {
  33. // IPv4 HTTPS (port 443)
  34. pkt4HTTPS := buildMockIPv4Packet(6, 50123, 443, 200)
  35. if !isEncryptedIPPacket(pkt4HTTPS) {
  36. t.Fatalf("expected IPv4 port 443 to be recognized as encrypted")
  37. }
  38. // IPv4 WireGuard (port 51820)
  39. pkt4WG := buildMockIPv4Packet(17, 51820, 50124, 200)
  40. if !isEncryptedIPPacket(pkt4WG) {
  41. t.Fatalf("expected IPv4 port 51820 to be recognized as encrypted")
  42. }
  43. // IPv4 HTTP (port 80) - Plaintext
  44. pkt4HTTP := buildMockIPv4Packet(6, 50125, 80, 200)
  45. if isEncryptedIPPacket(pkt4HTTP) {
  46. t.Fatalf("expected IPv4 port 80 to not be recognized as encrypted")
  47. }
  48. // IPv6 SSH (port 22)
  49. pkt6SSH := buildMockIPv6Packet(6, 22, 50126, 200)
  50. if !isEncryptedIPPacket(pkt6SSH) {
  51. t.Fatalf("expected IPv6 port 22 to be recognized as encrypted")
  52. }
  53. // IPv6 DNS (port 53) - Plaintext
  54. pkt6DNS := buildMockIPv6Packet(17, 50127, 53, 200)
  55. if isEncryptedIPPacket(pkt6DNS) {
  56. t.Fatalf("expected IPv6 port 53 to not be recognized as encrypted")
  57. }
  58. }
  59. func TestAdaptiveCompressor(t *testing.T) {
  60. ac := newAdaptiveCompressor(1420)
  61. // 1. Small packet (< 128 bytes) should be bypassed
  62. smallPkt := buildMockIPv4Packet(6, 50128, 80, 50)
  63. if _, ok := ac.Compress(smallPkt); ok {
  64. t.Fatalf("small packet should not be compressed")
  65. }
  66. // 2. Encrypted packet (port 443) should be bypassed without LZ4
  67. encPkt := buildMockIPv4Packet(6, 50129, 443, 300)
  68. if _, ok := ac.Compress(encPkt); ok {
  69. t.Fatalf("encrypted packet should not be compressed")
  70. }
  71. // 3. Compressible packet (e.g. repeated pattern) on port 80
  72. compressiblePayload := bytes.Repeat([]byte("ABCDEFGHIJ1234567890"), 30) // 600 bytes
  73. compPkt := buildMockIPv4Packet(6, 50130, 80, len(compressiblePayload))
  74. copy(compPkt[24:], compressiblePayload)
  75. out, ok := ac.Compress(compPkt)
  76. if !ok || len(out) >= len(compPkt)-16 {
  77. t.Fatalf("expected compressible packet to compress, got ok=%v, len=%d", ok, len(out))
  78. }
  79. // 4. Incompressible random packet stream triggers backoff
  80. incompPayload := []byte{
  81. 52, 56, 184, 144, 222, 129, 243, 220, 31, 151, 230, 149, 61, 206, 167, 169, 127, 181, 162, 174,
  82. 132, 186, 105, 47, 180, 106, 222, 232, 2, 220, 117, 243, 3, 5, 26, 229, 136, 49, 9, 249,
  83. 92, 89, 189, 80, 114, 199, 93, 244, 95, 21, 15, 212, 5, 241, 24, 101, 13, 94, 151, 67,
  84. 88, 236, 222, 216, 162, 133, 240, 45, 138, 47, 11, 225, 207, 45, 7, 99, 13, 23, 213, 53,
  85. 212, 12, 113, 51, 97, 247, 58, 49, 201, 108, 243, 54, 238, 153, 40, 189, 66, 149, 24, 83,
  86. 8, 184, 76, 123, 233, 190, 68, 207, 249, 87, 16, 54, 121, 80, 188, 45, 198, 21, 254, 107,
  87. 159, 52, 187, 220, 11, 159, 180, 235, 219, 209, 192, 60, 101, 201, 195, 137, 109, 195, 70, 124,
  88. 65, 79, 209, 48, 184, 253, 16, 132, 118, 15, 104, 50, 114, 157, 121, 62, 155, 141, 77, 82,
  89. 222, 58, 128, 0, 84, 157, 91, 156, 189, 239, 242, 109, 78, 187, 221, 204, 35, 201, 158, 94,
  90. 20, 199, 103, 136, 115, 17, 114, 255, 115, 46, 182, 194, 41, 25, 104, 56, 154, 244, 134, 158,
  91. }
  92. incompPkt := buildMockIPv4Packet(6, 50131, 80, len(incompPayload))
  93. copy(incompPkt[24:], incompPayload)
  94. // Fire 16 failures
  95. for i := 0; i < 16; i++ {
  96. _, ok := ac.Compress(incompPkt)
  97. if ok {
  98. t.Fatalf("random packet should not compress")
  99. }
  100. }
  101. // At this point, backoff should be triggered (skipCount == 256)
  102. if ac.skipCount != 256 {
  103. t.Fatalf("expected skipCount == 256, got %d", ac.skipCount)
  104. }
  105. // The next 256 calls should be skipped immediately
  106. for i := 0; i < 256; i++ {
  107. _, ok := ac.Compress(incompPkt)
  108. if ok {
  109. t.Fatalf("incompressible packet should be skipped during backoff")
  110. }
  111. }
  112. if ac.skipCount != 0 {
  113. t.Fatalf("expected skipCount to count down to 0, got %d", ac.skipCount)
  114. }
  115. }