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