github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/tests/ipv4_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 ipv4_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/header" 25 "github.com/SagerNet/gvisor/test/packetimpact/testbench" 26 ) 27 28 func init() { 29 testbench.Initialize(flag.CommandLine) 30 } 31 32 type fragmentInfo struct { 33 offset uint16 34 size uint16 35 more uint8 36 id uint16 37 } 38 39 func TestIPv4FragmentReassembly(t *testing.T) { 40 icmpv4ProtoNum := uint8(header.ICMPv4ProtocolNumber) 41 42 tests := []struct { 43 description string 44 ipPayloadLen int 45 fragments []fragmentInfo 46 expectReply bool 47 }{ 48 { 49 description: "basic reassembly", 50 ipPayloadLen: 3000, 51 fragments: []fragmentInfo{ 52 {offset: 0, size: 1000, id: 5, more: header.IPv4FlagMoreFragments}, 53 {offset: 1000, size: 1000, id: 5, more: header.IPv4FlagMoreFragments}, 54 {offset: 2000, size: 1000, id: 5, more: 0}, 55 }, 56 expectReply: true, 57 }, 58 { 59 description: "out of order fragments", 60 ipPayloadLen: 3000, 61 fragments: []fragmentInfo{ 62 {offset: 2000, size: 1000, id: 6, more: 0}, 63 {offset: 0, size: 1000, id: 6, more: header.IPv4FlagMoreFragments}, 64 {offset: 1000, size: 1000, id: 6, more: header.IPv4FlagMoreFragments}, 65 }, 66 expectReply: true, 67 }, 68 { 69 description: "duplicated fragments", 70 ipPayloadLen: 3000, 71 fragments: []fragmentInfo{ 72 {offset: 0, size: 1000, id: 7, more: header.IPv4FlagMoreFragments}, 73 {offset: 1000, size: 1000, id: 7, more: header.IPv4FlagMoreFragments}, 74 {offset: 1000, size: 1000, id: 7, more: header.IPv4FlagMoreFragments}, 75 {offset: 2000, size: 1000, id: 7, more: 0}, 76 }, 77 expectReply: true, 78 }, 79 { 80 description: "fragment subset", 81 ipPayloadLen: 3000, 82 fragments: []fragmentInfo{ 83 {offset: 0, size: 1000, id: 8, more: header.IPv4FlagMoreFragments}, 84 {offset: 1000, size: 1000, id: 8, more: header.IPv4FlagMoreFragments}, 85 {offset: 512, size: 256, id: 8, more: header.IPv4FlagMoreFragments}, 86 {offset: 2000, size: 1000, id: 8, more: 0}, 87 }, 88 expectReply: true, 89 }, 90 { 91 description: "fragment overlap", 92 ipPayloadLen: 3000, 93 fragments: []fragmentInfo{ 94 {offset: 0, size: 1000, id: 9, more: header.IPv4FlagMoreFragments}, 95 {offset: 1512, size: 1000, id: 9, more: header.IPv4FlagMoreFragments}, 96 {offset: 1000, size: 1000, id: 9, more: header.IPv4FlagMoreFragments}, 97 {offset: 2000, size: 1000, id: 9, more: 0}, 98 }, 99 expectReply: false, 100 }, 101 } 102 103 for _, test := range tests { 104 t.Run(test.description, func(t *testing.T) { 105 dut := testbench.NewDUT(t) 106 conn := dut.Net.NewIPv4Conn(t, testbench.IPv4{}, testbench.IPv4{}) 107 defer conn.Close(t) 108 109 data := make([]byte, test.ipPayloadLen) 110 icmp := header.ICMPv4(data[:header.ICMPv4MinimumSize]) 111 icmp.SetType(header.ICMPv4Echo) 112 icmp.SetCode(header.ICMPv4UnusedCode) 113 icmp.SetChecksum(0) 114 icmp.SetSequence(0) 115 icmp.SetIdent(0) 116 originalPayload := data[header.ICMPv4MinimumSize:] 117 if _, err := rand.Read(originalPayload); err != nil { 118 t.Fatalf("rand.Read: %s", err) 119 } 120 cksum := header.ICMPv4Checksum(icmp, header.Checksum(originalPayload, 0 /* initial */)) 121 icmp.SetChecksum(cksum) 122 123 for _, fragment := range test.fragments { 124 conn.Send(t, 125 testbench.IPv4{ 126 Protocol: &icmpv4ProtoNum, 127 FragmentOffset: testbench.Uint16(fragment.offset), 128 Flags: testbench.Uint8(fragment.more), 129 ID: testbench.Uint16(fragment.id), 130 }, 131 &testbench.Payload{ 132 Bytes: data[fragment.offset:][:fragment.size], 133 }) 134 } 135 136 var bytesReceived int 137 reassembledPayload := make([]byte, test.ipPayloadLen) 138 // We are sending a packet fragmented into smaller parts but the 139 // response may also be large enough to require fragmentation. 140 // Therefore we only look for payload for an IPv4 packet not ICMP. 141 for { 142 incomingFrame, err := conn.ExpectFrame(t, testbench.Layers{ 143 &testbench.Ether{}, 144 &testbench.IPv4{}, 145 }, time.Second) 146 if err != nil { 147 // Either an unexpected frame was received, or none at all. 148 if test.expectReply && bytesReceived < test.ipPayloadLen { 149 t.Fatalf("received %d bytes out of %d, then conn.ExpectFrame(_, _, time.Second) failed with %s", bytesReceived, test.ipPayloadLen, err) 150 } 151 break 152 } 153 if !test.expectReply { 154 t.Fatalf("unexpected reply received:\n%s", incomingFrame) 155 } 156 // We only asked for Ethernet and IPv4 so the rest should be payload. 157 ipPayload, err := incomingFrame[2 /* Payload */].ToBytes() 158 if err != nil { 159 t.Fatalf("failed to parse payload: incomingPacket[2].ToBytes() = (_, %s)", err) 160 } 161 offset := *incomingFrame[1 /* IPv4 */].(*testbench.IPv4).FragmentOffset 162 if copied := copy(reassembledPayload[offset:], ipPayload); copied != len(ipPayload) { 163 t.Fatalf("wrong number of bytes copied into reassembledPayload: got = %d, want = %d", copied, len(ipPayload)) 164 } 165 bytesReceived += len(ipPayload) 166 } 167 168 if test.expectReply { 169 if diff := cmp.Diff(originalPayload, reassembledPayload[header.ICMPv4MinimumSize:]); diff != "" { 170 t.Fatalf("reassembledPayload mismatch (-want +got):\n%s", diff) 171 } 172 } 173 }) 174 } 175 }