gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/tests/ipv6_fragment_reassembly_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_reassembly_test 16 17 import ( 18 "flag" 19 "math/rand" 20 "testing" 21 "time" 22 23 "github.com/google/go-cmp/cmp" 24 "gvisor.dev/gvisor/pkg/tcpip" 25 "gvisor.dev/gvisor/pkg/tcpip/checksum" 26 "gvisor.dev/gvisor/pkg/tcpip/header" 27 "gvisor.dev/gvisor/test/packetimpact/testbench" 28 ) 29 30 func init() { 31 testbench.Initialize(flag.CommandLine) 32 } 33 34 type fragmentInfo struct { 35 offset uint16 36 size uint16 37 more bool 38 id uint32 39 } 40 41 func TestIPv6FragmentReassembly(t *testing.T) { 42 icmpv6ProtoNum := header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber) 43 44 tests := []struct { 45 description string 46 ipPayloadLen int 47 fragments []fragmentInfo 48 expectReply bool 49 }{ 50 { 51 description: "basic reassembly", 52 ipPayloadLen: 3000, 53 fragments: []fragmentInfo{ 54 {offset: 0, size: 1000, id: 100, more: true}, 55 {offset: 1000, size: 1000, id: 100, more: true}, 56 {offset: 2000, size: 1000, id: 100, more: false}, 57 }, 58 expectReply: true, 59 }, 60 { 61 description: "out of order fragments", 62 ipPayloadLen: 3000, 63 fragments: []fragmentInfo{ 64 {offset: 0, size: 1000, id: 101, more: true}, 65 {offset: 2000, size: 1000, id: 101, more: false}, 66 {offset: 1000, size: 1000, id: 101, more: true}, 67 }, 68 expectReply: true, 69 }, 70 { 71 description: "duplicated fragments", 72 ipPayloadLen: 3000, 73 fragments: []fragmentInfo{ 74 {offset: 0, size: 1000, id: 102, more: true}, 75 {offset: 1000, size: 1000, id: 102, more: true}, 76 {offset: 1000, size: 1000, id: 102, more: true}, 77 {offset: 2000, size: 1000, id: 102, more: false}, 78 }, 79 expectReply: true, 80 }, 81 { 82 description: "fragment subset", 83 ipPayloadLen: 3000, 84 fragments: []fragmentInfo{ 85 {offset: 0, size: 1000, id: 103, more: true}, 86 {offset: 1000, size: 1000, id: 103, more: true}, 87 {offset: 512, size: 256, id: 103, more: true}, 88 {offset: 2000, size: 1000, id: 103, more: false}, 89 }, 90 expectReply: true, 91 }, 92 { 93 description: "fragment overlap", 94 ipPayloadLen: 3000, 95 fragments: []fragmentInfo{ 96 {offset: 0, size: 1000, id: 104, more: true}, 97 {offset: 1512, size: 1000, id: 104, more: true}, 98 {offset: 1000, size: 1000, id: 104, more: true}, 99 {offset: 2000, size: 1000, id: 104, more: false}, 100 }, 101 expectReply: false, 102 }, 103 } 104 105 for _, test := range tests { 106 t.Run(test.description, func(t *testing.T) { 107 dut := testbench.NewDUT(t) 108 conn := dut.Net.NewIPv6Conn(t, testbench.IPv6{}, testbench.IPv6{}) 109 defer conn.Close(t) 110 111 lIP := tcpip.AddrFrom16Slice(dut.Net.LocalIPv6) 112 rIP := tcpip.AddrFrom16Slice(dut.Net.RemoteIPv6) 113 114 data := make([]byte, test.ipPayloadLen) 115 icmp := header.ICMPv6(data[:header.ICMPv6HeaderSize]) 116 icmp.SetType(header.ICMPv6EchoRequest) 117 icmp.SetCode(header.ICMPv6UnusedCode) 118 icmp.SetChecksum(0) 119 originalPayload := data[header.ICMPv6HeaderSize:] 120 if _, err := rand.Read(originalPayload); err != nil { 121 t.Fatalf("rand.Read: %s", err) 122 } 123 124 cksum := header.ICMPv6Checksum(header.ICMPv6ChecksumParams{ 125 Header: icmp, 126 Src: lIP, 127 Dst: rIP, 128 PayloadCsum: checksum.Checksum(originalPayload, 0 /* initial */), 129 PayloadLen: len(originalPayload), 130 }) 131 icmp.SetChecksum(cksum) 132 133 for _, fragment := range test.fragments { 134 conn.Send(t, testbench.IPv6{}, 135 &testbench.IPv6FragmentExtHdr{ 136 NextHeader: &icmpv6ProtoNum, 137 FragmentOffset: testbench.Uint16(fragment.offset / header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit), 138 MoreFragments: testbench.Bool(fragment.more), 139 Identification: testbench.Uint32(fragment.id), 140 }, 141 &testbench.Payload{ 142 Bytes: data[fragment.offset:][:fragment.size], 143 }) 144 } 145 146 var bytesReceived int 147 reassembledPayload := make([]byte, test.ipPayloadLen) 148 for { 149 incomingFrame, err := conn.ExpectFrame(t, testbench.Layers{ 150 &testbench.Ether{}, 151 &testbench.IPv6{}, 152 &testbench.IPv6FragmentExtHdr{}, 153 }, time.Second) 154 if err != nil { 155 // Either an unexpected frame was received, or none at all. 156 if test.expectReply && bytesReceived < test.ipPayloadLen { 157 t.Fatalf("received %d bytes out of %d, then conn.ExpectFrame(_, _, time.Second) failed with %s", bytesReceived, test.ipPayloadLen, err) 158 } 159 break 160 } 161 if !test.expectReply { 162 t.Fatalf("unexpected reply received:\n%s", incomingFrame) 163 } 164 ipPayload, err := incomingFrame[3 /* Payload */].ToBytes() 165 if err != nil { 166 t.Fatalf("failed to parse ICMPv6 header: incomingPacket[3].ToBytes() = (_, %s)", err) 167 } 168 offset := *incomingFrame[2 /* IPv6FragmentExtHdr */].(*testbench.IPv6FragmentExtHdr).FragmentOffset 169 offset *= header.IPv6FragmentExtHdrFragmentOffsetBytesPerUnit 170 if copied := copy(reassembledPayload[offset:], ipPayload); copied != len(ipPayload) { 171 t.Fatalf("wrong number of bytes copied into reassembledPayload: got = %d, want = %d", copied, len(ipPayload)) 172 } 173 bytesReceived += len(ipPayload) 174 } 175 176 if test.expectReply { 177 if diff := cmp.Diff(originalPayload, reassembledPayload[header.ICMPv6HeaderSize:]); diff != "" { 178 t.Fatalf("reassembledPayload mismatch (-want +got):\n%s", diff) 179 } 180 } 181 }) 182 } 183 }