github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/tests/ipv6_fragment_icmp_error_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ipv6_fragment_icmp_error_test 16 17 import ( 18 "flag" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/SagerNet/gvisor/pkg/tcpip" 24 "github.com/SagerNet/gvisor/pkg/tcpip/header" 25 "github.com/SagerNet/gvisor/pkg/tcpip/network/ipv6" 26 "github.com/SagerNet/gvisor/test/packetimpact/testbench" 27 ) 28 29 const ( 30 data = "IPV6_PROTOCOL_TESTER_FOR_FRAGMENT" 31 fragmentID = 1 32 reassemblyTimeout = ipv6.ReassembleTimeout + 5*time.Second 33 ) 34 35 func init() { 36 testbench.Initialize(flag.CommandLine) 37 } 38 39 func fragmentedICMPEchoRequest(t *testing.T, n *testbench.DUTTestNet, conn *testbench.IPv6Conn, firstPayloadLength uint16, payload []byte, secondFragmentOffset uint16) ([]testbench.Layers, [][]byte) { 40 t.Helper() 41 42 icmpv6Header := header.ICMPv6(make([]byte, header.ICMPv6EchoMinimumSize)) 43 icmpv6Header.SetType(header.ICMPv6EchoRequest) 44 icmpv6Header.SetCode(header.ICMPv6UnusedCode) 45 icmpv6Header.SetIdent(0) 46 icmpv6Header.SetSequence(0) 47 cksum := header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 48 Header: icmpv6Header, 49 Src: tcpip.Address(n.LocalIPv6), 50 Dst: tcpip.Address(n.RemoteIPv6), 51 PayloadCsum: header.Checksum(payload, 0 /* initial */), 52 PayloadLen: len(payload), 53 }) 54 icmpv6Header.SetChecksum(cksum) 55 icmpv6Bytes := append([]byte(icmpv6Header), payload...) 56 57 icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber) 58 59 firstFragment := conn.CreateFrame(t, testbench.Layers{&testbench.IPv6{}}, 60 &testbench.IPv6FragmentExtHdr{ 61 NextHeader: &icmpv6ProtoNum, 62 FragmentOffset: testbench.Uint16(0), 63 MoreFragments: testbench.Bool(true), 64 Identification: testbench.Uint32(fragmentID), 65 }, 66 &testbench.Payload{ 67 Bytes: icmpv6Bytes[:header.ICMPv6PayloadOffset+firstPayloadLength], 68 }, 69 ) 70 firstIPv6 := firstFragment[1:] 71 firstIPv6Bytes, err := firstIPv6.ToBytes() 72 if err != nil { 73 t.Fatalf("failed to convert first %s to bytes: %s", firstIPv6, err) 74 } 75 76 secondFragment := conn.CreateFrame(t, testbench.Layers{&testbench.IPv6{}}, 77 &testbench.IPv6FragmentExtHdr{ 78 NextHeader: &icmpv6ProtoNum, 79 FragmentOffset: testbench.Uint16(secondFragmentOffset), 80 MoreFragments: testbench.Bool(false), 81 Identification: testbench.Uint32(fragmentID), 82 }, 83 &testbench.Payload{ 84 Bytes: icmpv6Bytes[header.ICMPv6PayloadOffset+firstPayloadLength:], 85 }, 86 ) 87 secondIPv6 := secondFragment[1:] 88 secondIPv6Bytes, err := secondIPv6.ToBytes() 89 if err != nil { 90 t.Fatalf("failed to convert second %s to bytes: %s", secondIPv6, err) 91 } 92 93 return []testbench.Layers{firstFragment, secondFragment}, [][]byte{firstIPv6Bytes, secondIPv6Bytes} 94 } 95 96 func TestIPv6ICMPEchoRequestFragmentReassembly(t *testing.T) { 97 tests := []struct { 98 name string 99 firstPayloadLength uint16 100 payload []byte 101 secondFragmentOffset uint16 102 sendFrameOrder []int 103 }{ 104 { 105 name: "reassemble two fragments", 106 firstPayloadLength: 8, 107 payload: []byte(data)[:20], 108 secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8, 109 sendFrameOrder: []int{1, 2}, 110 }, 111 { 112 name: "reassemble two fragments in reverse order", 113 firstPayloadLength: 8, 114 payload: []byte(data)[:20], 115 secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8, 116 sendFrameOrder: []int{2, 1}, 117 }, 118 } 119 120 for _, test := range tests { 121 t.Run(test.name, func(t *testing.T) { 122 t.Parallel() 123 dut := testbench.NewDUT(t) 124 conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) 125 defer conn.Close(t) 126 127 fragments, _ := fragmentedICMPEchoRequest(t, dut.Net, &conn, test.firstPayloadLength, test.payload, test.secondFragmentOffset) 128 129 for _, i := range test.sendFrameOrder { 130 conn.SendFrame(t, fragments[i-1]) 131 } 132 133 gotEchoReply, err := conn.ExpectFrame(t, testbench.Layers{ 134 &testbench.Ether{}, 135 &testbench.IPv6{}, 136 &testbench.ICMPv6{ 137 Type: testbench.ICMPv6Type(header.ICMPv6EchoReply), 138 Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode), 139 }, 140 }, time.Second) 141 if err != nil { 142 t.Fatalf("didn't receive an ICMPv6 Echo Reply: %s", err) 143 } 144 gotPayload, err := gotEchoReply[len(gotEchoReply)-1].ToBytes() 145 if err != nil { 146 t.Fatalf("failed to convert ICMPv6 to bytes: %s", err) 147 } 148 icmpPayload := gotPayload[header.ICMPv6EchoMinimumSize:] 149 wantPayload := test.payload 150 if diff := cmp.Diff(wantPayload, icmpPayload); diff != "" { 151 t.Fatalf("payload mismatch (-want +got):\n%s", diff) 152 } 153 }) 154 } 155 } 156 157 func TestIPv6FragmentReassemblyTimeout(t *testing.T) { 158 type icmpFramePattern struct { 159 typ header.ICMPv6Type 160 code header.ICMPv6Code 161 } 162 163 type icmpReassemblyTimeoutDetail struct { 164 payloadFragment int // 1: first fragment, 2: second fragnemt. 165 } 166 167 tests := []struct { 168 name string 169 firstPayloadLength uint16 170 payload []byte 171 secondFragmentOffset uint16 172 sendFrameOrder []int 173 replyFilter icmpFramePattern 174 expectErrorReply bool 175 expectICMPReassemblyTimeout icmpReassemblyTimeoutDetail 176 }{ 177 { 178 name: "reassembly timeout (first fragment only)", 179 firstPayloadLength: 8, 180 payload: []byte(data)[:20], 181 secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8, 182 sendFrameOrder: []int{1}, 183 replyFilter: icmpFramePattern{ 184 typ: header.ICMPv6TimeExceeded, 185 code: header.ICMPv6ReassemblyTimeout, 186 }, 187 expectErrorReply: true, 188 expectICMPReassemblyTimeout: icmpReassemblyTimeoutDetail{ 189 payloadFragment: 1, 190 }, 191 }, 192 { 193 name: "reassembly timeout (second fragment only)", 194 firstPayloadLength: 8, 195 payload: []byte(data)[:20], 196 secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8, 197 sendFrameOrder: []int{2}, 198 replyFilter: icmpFramePattern{ 199 typ: header.ICMPv6TimeExceeded, 200 code: header.ICMPv6ReassemblyTimeout, 201 }, 202 expectErrorReply: false, 203 }, 204 { 205 name: "reassembly timeout (two fragments with a gap)", 206 firstPayloadLength: 8, 207 payload: []byte(data)[:20], 208 secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 16) / 8, 209 sendFrameOrder: []int{1, 2}, 210 replyFilter: icmpFramePattern{ 211 typ: header.ICMPv6TimeExceeded, 212 code: header.ICMPv6ReassemblyTimeout, 213 }, 214 expectErrorReply: true, 215 expectICMPReassemblyTimeout: icmpReassemblyTimeoutDetail{ 216 payloadFragment: 1, 217 }, 218 }, 219 } 220 221 for _, test := range tests { 222 t.Run(test.name, func(t *testing.T) { 223 t.Parallel() 224 dut := testbench.NewDUT(t) 225 conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) 226 defer conn.Close(t) 227 228 fragments, ipv6Bytes := fragmentedICMPEchoRequest(t, dut.Net, &conn, test.firstPayloadLength, test.payload, test.secondFragmentOffset) 229 230 for _, i := range test.sendFrameOrder { 231 conn.SendFrame(t, fragments[i-1]) 232 } 233 234 gotErrorMessage, err := conn.ExpectFrame(t, testbench.Layers{ 235 &testbench.Ether{}, 236 &testbench.IPv6{}, 237 &testbench.ICMPv6{ 238 Type: testbench.ICMPv6Type(test.replyFilter.typ), 239 Code: testbench.ICMPv6Code(test.replyFilter.code), 240 }, 241 }, reassemblyTimeout) 242 if !test.expectErrorReply { 243 if err == nil { 244 t.Fatalf("shouldn't receive an ICMPv6 Error Message with type=%d and code=%d", test.replyFilter.typ, test.replyFilter.code) 245 } 246 return 247 } 248 if err != nil { 249 t.Fatalf("didn't receive an ICMPv6 Error Message with type=%d and code=%d: err", test.replyFilter.typ, test.replyFilter.code, err) 250 } 251 gotPayload, err := gotErrorMessage[len(gotErrorMessage)-1].ToBytes() 252 if err != nil { 253 t.Fatalf("failed to convert ICMPv6 to bytes: %s", err) 254 } 255 icmpPayload := gotPayload[header.ICMPv6ErrorHeaderSize:] 256 wantPayload := ipv6Bytes[test.expectICMPReassemblyTimeout.payloadFragment-1] 257 if diff := cmp.Diff(wantPayload, icmpPayload); diff != "" { 258 t.Fatalf("payload mismatch (-want +got):\n%s", diff) 259 } 260 }) 261 } 262 } 263 264 func TestIPv6FragmentParamProblem(t *testing.T) { 265 type icmpFramePattern struct { 266 typ header.ICMPv6Type 267 code header.ICMPv6Code 268 } 269 270 type icmpParamProblemDetail struct { 271 pointer uint32 272 payloadFragment int // 1: first fragment, 2: second fragnemt. 273 } 274 275 tests := []struct { 276 name string 277 firstPayloadLength uint16 278 payload []byte 279 secondFragmentOffset uint16 280 sendFrameOrder []int 281 replyFilter icmpFramePattern 282 expectICMPParamProblem icmpParamProblemDetail 283 }{ 284 { 285 name: "payload size not a multiple of 8", 286 firstPayloadLength: 9, 287 payload: []byte(data)[:20], 288 secondFragmentOffset: (header.ICMPv6EchoMinimumSize + 8) / 8, 289 sendFrameOrder: []int{1}, 290 replyFilter: icmpFramePattern{ 291 typ: header.ICMPv6ParamProblem, 292 code: header.ICMPv6ErroneousHeader, 293 }, 294 expectICMPParamProblem: icmpParamProblemDetail{ 295 pointer: 4, 296 payloadFragment: 1, 297 }, 298 }, 299 { 300 name: "payload length error", 301 firstPayloadLength: 16, 302 payload: []byte(data)[:33], 303 secondFragmentOffset: 65520 / 8, 304 sendFrameOrder: []int{1, 2}, 305 replyFilter: icmpFramePattern{ 306 typ: header.ICMPv6ParamProblem, 307 code: header.ICMPv6ErroneousHeader, 308 }, 309 expectICMPParamProblem: icmpParamProblemDetail{ 310 pointer: 42, 311 payloadFragment: 2, 312 }, 313 }, 314 } 315 316 for _, test := range tests { 317 t.Run(test.name, func(t *testing.T) { 318 t.Parallel() 319 dut := testbench.NewDUT(t) 320 conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) 321 defer conn.Close(t) 322 323 fragments, ipv6Bytes := fragmentedICMPEchoRequest(t, dut.Net, &conn, test.firstPayloadLength, test.payload, test.secondFragmentOffset) 324 325 for _, i := range test.sendFrameOrder { 326 conn.SendFrame(t, fragments[i-1]) 327 } 328 329 gotErrorMessage, err := conn.ExpectFrame(t, testbench.Layers{ 330 &testbench.Ether{}, 331 &testbench.IPv6{}, 332 &testbench.ICMPv6{ 333 Type: testbench.ICMPv6Type(test.replyFilter.typ), 334 Code: testbench.ICMPv6Code(test.replyFilter.code), 335 }, 336 }, time.Second) 337 if err != nil { 338 t.Fatalf("didn't receive an ICMPv6 Error Message with type=%d and code=%d: err", test.replyFilter.typ, test.replyFilter.code, err) 339 } 340 gotPayload, err := gotErrorMessage[len(gotErrorMessage)-1].ToBytes() 341 if err != nil { 342 t.Fatalf("failed to convert ICMPv6 to bytes: %s", err) 343 } 344 gotPointer := header.ICMPv6(gotPayload).TypeSpecific() 345 wantPointer := test.expectICMPParamProblem.pointer 346 if gotPointer != wantPointer { 347 t.Fatalf("got pointer = %d, want = %d", gotPointer, wantPointer) 348 } 349 icmpPayload := gotPayload[header.ICMPv6ErrorHeaderSize:] 350 wantPayload := ipv6Bytes[test.expectICMPParamProblem.payloadFragment-1] 351 if diff := cmp.Diff(wantPayload, icmpPayload); diff != "" { 352 t.Fatalf("payload mismatch (-want +got):\n%s", diff) 353 } 354 }) 355 } 356 }