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