github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/packetman/packetman_test.go (about) 1 /* 2 * Copyright (c) 2020, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package packetman 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "net" 26 "testing" 27 28 "github.com/google/gopacket" 29 "github.com/google/gopacket/layers" 30 ) 31 32 func TestTransformations(t *testing.T) { 33 34 // Test: apply various transformations to an original packet, then parse the 35 // resulting packets and check that flags/fields/options are as expected. 36 37 // Limitation: gopacket, used here in the test to verify transformations, 38 // will fail to parse some or all of certain packets that can be created by 39 // certain transformations. gopacket will fail to parse packets with too many 40 // option bytes or invalid DataOffset values. gopacket will stop 41 // deserializing TCP options as soon as it encounters the EOL option, even if 42 // the packet actually contains more options. Etc. 43 44 specJSON := []byte(` 45 { 46 "Name": "test-spec", 47 "PacketSpecs": [ 48 ["TCP-flags SA", 49 "TCP-flags S", 50 "TCP-srcport ffff", 51 "TCP-dstport ffff", 52 "TCP-seq ffffffff", 53 "TCP-ack ffffffff", 54 "TCP-dataoffset 0f", 55 "TCP-window ffff", 56 "TCP-checksum ffff", 57 "TCP-urgent ffff", 58 "TCP-option-nop omit", 59 "TCP-option-mss ffff", 60 "TCP-option-windowscale ff", 61 "TCP-option-sackpermitted ", 62 "TCP-option-sack ffffffffffffffff", 63 "TCP-option-timestamps ffffffffffffffff", 64 "TCP-payload eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 65 "TCP-payload ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"], 66 67 ["TCP-flags random", 68 "TCP-srcport random", 69 "TCP-dstport random", 70 "TCP-seq random", 71 "TCP-ack random", 72 "TCP-dataoffset random", 73 "TCP-window random", 74 "TCP-checksum random", 75 "TCP-urgent random", 76 "TCP-option-mss random", 77 "TCP-option-windowscale random", 78 "TCP-option-sackpermitted random", 79 "TCP-option-timestamps random", 80 "TCP-payload random"] 81 ] 82 } 83 `) 84 85 var spec *Spec 86 err := json.Unmarshal(specJSON, &spec) 87 if err != nil { 88 t.Fatalf("json.Unmarshal failed: %v", err) 89 } 90 91 c, err := compileSpec(spec) 92 if err != nil { 93 t.Fatalf("compileSpec failed: %v", err) 94 } 95 96 if c.name != spec.Name { 97 t.Fatalf("unexpected compiled spec name: %s", c.name) 98 } 99 100 originalIPv4 := &layers.IPv4{ 101 Version: 0x04, 102 IHL: 0x05, 103 Protocol: 0x06, 104 SrcIP: net.IPv4(192, 168, 0, 1), 105 DstIP: net.IPv4(192, 168, 0, 2), 106 } 107 108 originalTCP := &layers.TCP{ 109 SYN: true, 110 ACK: true, 111 Options: []layers.TCPOption{ 112 layers.TCPOption{OptionType: layers.TCPOptionKindNop, OptionLength: 1}, 113 layers.TCPOption{OptionType: layers.TCPOptionKindSACKPermitted, OptionLength: 2}, 114 layers.TCPOption{OptionType: layers.TCPOptionKindSACK, OptionLength: 10, OptionData: bytes.Repeat([]byte{0}, 8)}, 115 layers.TCPOption{OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: bytes.Repeat([]byte{0}, 8)}, 116 }, 117 } 118 119 originalTCP.SetNetworkLayerForChecksum(originalIPv4) 120 121 originalPayload := gopacket.Payload([]byte{0, 0, 0, 0}) 122 123 buffer := gopacket.NewSerializeBuffer() 124 gopacket.SerializeLayers( 125 buffer, 126 gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}, 127 originalIPv4, 128 originalTCP, 129 originalPayload) 130 originalPacketData := buffer.Bytes() 131 132 originalPacket := gopacket.NewPacket(originalPacketData, layers.LayerTypeIPv4, gopacket.Default) 133 errLayer := originalPacket.ErrorLayer() 134 if errLayer != nil { 135 t.Fatalf("gopacket.NewPacket failed: %v", errLayer.Error()) 136 } 137 138 stripEOLOption(originalPacket) 139 140 repeats := 1000 141 repeatLoop: 142 for i := 0; i < repeats; i++ { 143 144 lastRepeat := i == repeats-1 145 146 injectPackets, err := c.apply(originalPacket) 147 if err != nil { 148 t.Fatalf("apply failed: %v", err) 149 } 150 151 if len(injectPackets) != 2 { 152 t.Fatalf("unexpected injectPackets count: %d", len(injectPackets)) 153 } 154 155 for packetNum, packetData := range injectPackets { 156 157 packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default) 158 159 errLayer := packet.ErrorLayer() 160 if errLayer != nil { 161 t.Fatalf("gopacket.NewPacket failed: %v", errLayer.Error()) 162 } 163 164 tcpLayer := packet.Layer(layers.LayerTypeTCP) 165 if tcpLayer == nil { 166 t.Fatalf("missing TCP layer") 167 } 168 169 tcp := tcpLayer.(*layers.TCP) 170 171 payloadLayer := packet.Layer(gopacket.LayerTypePayload) 172 if payloadLayer == nil { 173 t.Fatalf("missing payload layer") 174 } 175 176 payload := payloadLayer.(*gopacket.Payload) 177 178 optionsEqual := func(a, b layers.TCPOption) bool { 179 if a.OptionType != b.OptionType || 180 a.OptionLength != b.OptionLength || 181 !bytes.Equal(a.OptionData, b.OptionData) { 182 return false 183 } 184 return true 185 } 186 187 optionsListEqual := func(a, b []layers.TCPOption) bool { 188 if len(a) != len(b) { 189 return false 190 } 191 for i, o := range a { 192 if !optionsEqual(o, b[i]) { 193 return false 194 } 195 } 196 return true 197 } 198 199 if packetNum == 0 { 200 201 // With multiple, redundant value specs (TCP-flags in the test case) the 202 // _last_ value spec should be applied. Values should be truncated to 203 // protocol lengths. The NOP option in the original packet should be 204 // omitted. 205 206 expectedOptions := []layers.TCPOption{ 207 layers.TCPOption{OptionType: layers.TCPOptionKindSACKPermitted, OptionLength: 2}, 208 layers.TCPOption{OptionType: layers.TCPOptionKindSACK, OptionLength: 10, OptionData: bytes.Repeat([]byte{0xff}, 8)}, 209 layers.TCPOption{OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: bytes.Repeat([]byte{0xff}, 8)}, 210 layers.TCPOption{OptionType: layers.TCPOptionKindMSS, OptionLength: 4, OptionData: bytes.Repeat([]byte{0xff}, 2)}, 211 layers.TCPOption{OptionType: layers.TCPOptionKindWindowScale, OptionLength: 3, OptionData: bytes.Repeat([]byte{0xff}, 1)}, 212 layers.TCPOption{OptionType: layers.TCPOptionKindEndList, OptionLength: 1}, 213 } 214 215 if tcp.SrcPort != 0xffff || 216 tcp.DstPort != 0xffff || 217 tcp.Seq != 0xffffffff || 218 tcp.Ack != 0xffffffff || 219 tcp.FIN || !tcp.SYN || tcp.RST || tcp.PSH || tcp.ACK || 220 tcp.URG || tcp.ECE || tcp.CWR || tcp.NS || 221 tcp.Window != 0xffff || 222 tcp.Urgent != 0xffff || 223 !optionsListEqual(tcp.Options, expectedOptions) { 224 t.Fatalf("unexpected TCP layer: %+v", tcp) 225 } 226 227 expectedPayload := bytes.Repeat([]byte{0xff}, 32) 228 if !bytes.Equal(expectedPayload, *payload) { 229 t.Fatalf("unexpected payload: %x", *payload) 230 } 231 232 } else { 233 234 // In at least one repeat, randomized fields fully differ from original, 235 // including zero-values; original NOP and SACK options retained; random 236 // options have correct protocol lengths. 237 238 if tcp.SrcPort == originalTCP.SrcPort || 239 tcp.DstPort == originalTCP.DstPort || 240 tcp.Seq == originalTCP.Seq || 241 tcp.Ack == originalTCP.Ack || 242 (tcp.FIN == originalTCP.FIN && 243 tcp.SYN == originalTCP.SYN && 244 tcp.RST == originalTCP.RST && 245 tcp.PSH == originalTCP.PSH && 246 tcp.ACK == originalTCP.ACK && 247 tcp.URG == originalTCP.URG && 248 tcp.ECE == originalTCP.ECE && 249 tcp.CWR == originalTCP.CWR && 250 tcp.NS == originalTCP.NS) || 251 tcp.Window == originalTCP.Window || 252 tcp.Checksum == originalTCP.Checksum || 253 tcp.Urgent == originalTCP.Urgent || 254 len(tcp.Options) != 7 || 255 !optionsEqual(tcp.Options[0], originalTCP.Options[0]) || 256 !optionsEqual(tcp.Options[1], originalTCP.Options[1]) || 257 !optionsEqual(tcp.Options[2], originalTCP.Options[2]) || 258 tcp.Options[3].OptionType != layers.TCPOptionKindTimestamps || 259 tcp.Options[3].OptionLength != 10 || 260 optionsEqual(tcp.Options[3], originalTCP.Options[3]) || 261 tcp.Options[4].OptionType != layers.TCPOptionKindMSS || 262 tcp.Options[4].OptionLength != 4 || 263 tcp.Options[5].OptionType != layers.TCPOptionKindWindowScale || 264 tcp.Options[5].OptionLength != 3 || 265 tcp.Options[6].OptionType != layers.TCPOptionKindEndList || 266 bytes.Equal(originalPayload, *payload) { 267 268 if lastRepeat { 269 t.Fatalf("unexpected TCP layer: %+v", tcp) 270 } 271 } else { 272 break repeatLoop 273 } 274 } 275 } 276 } 277 }