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