gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/tests/ipv4_id_uniqueness_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_id_uniqueness_test 16 17 import ( 18 "context" 19 "flag" 20 "fmt" 21 "testing" 22 "time" 23 24 "golang.org/x/sys/unix" 25 "gvisor.dev/gvisor/pkg/abi/linux" 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 func recvTCPSegment(t *testing.T, conn *testbench.TCPIPv4, expect *testbench.TCP, expectPayload *testbench.Payload) (uint16, error) { 35 layers, err := conn.ExpectData(t, expect, expectPayload, time.Second) 36 if err != nil { 37 return 0, fmt.Errorf("failed to receive TCP segment: %s", err) 38 } 39 if len(layers) < 2 { 40 return 0, fmt.Errorf("got packet with layers: %v, expected to have at least 2 layers (link and network)", layers) 41 } 42 ipv4, ok := layers[1].(*testbench.IPv4) 43 if !ok { 44 return 0, fmt.Errorf("got network layer: %T, expected: *IPv4", layers[1]) 45 } 46 if *ipv4.Flags&header.IPv4FlagDontFragment != 0 { 47 return 0, fmt.Errorf("got IPv4 DF=1, expected DF=0") 48 } 49 return *ipv4.ID, nil 50 } 51 52 // RFC 6864 section 4.2 states: "The IPv4 ID of non-atomic datagrams MUST NOT 53 // be reused when sending a copy of an earlier non-atomic datagram." 54 // 55 // This test creates a TCP connection, uses the IP_MTU_DISCOVER socket option 56 // to force the DF bit to be 0, and checks that a retransmitted segment has a 57 // different IPv4 Identification value than the original segment. 58 func TestIPv4RetransmitIdentificationUniqueness(t *testing.T) { 59 for _, tc := range []struct { 60 name string 61 payload []byte 62 }{ 63 {"SmallPayload", []byte("sample data")}, 64 // 512 bytes is chosen because sending more than this in a single segment 65 // causes the retransmission to send less than the original amount. 66 {"512BytePayload", testbench.GenerateRandomPayload(t, 512)}, 67 } { 68 t.Run(tc.name, func(t *testing.T) { 69 dut := testbench.NewDUT(t) 70 listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) 71 defer dut.Close(t, listenFD) 72 73 conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) 74 defer conn.Close(t) 75 76 conn.Connect(t) 77 remoteFD, _ := dut.Accept(t, listenFD) 78 defer dut.Close(t, remoteFD) 79 80 dut.SetSockOptInt(t, remoteFD, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1) 81 82 // TODO(b/129291778) The following socket option clears the DF bit on 83 // IP packets sent over the socket, and is currently not supported by 84 // gVisor. gVisor by default sends packets with DF=0 anyway, so the 85 // socket option being not supported does not affect the operation of 86 // this test. Once the socket option is supported, the following call 87 // can be changed to simply assert success. 88 ret, errno := dut.SetSockOptIntWithErrno(context.Background(), t, remoteFD, unix.IPPROTO_IP, linux.IP_MTU_DISCOVER, linux.IP_PMTUDISC_DONT) 89 // Fuchsia will return ENOPROTOPT errno. 90 if ret == -1 && errno != unix.ENOPROTOOPT { 91 t.Fatalf("failed to set IP_MTU_DISCOVER socket option to IP_PMTUDISC_DONT: %s", errno) 92 } 93 94 samplePayload := &testbench.Payload{Bytes: tc.payload} 95 96 dut.Send(t, remoteFD, tc.payload, 0) 97 if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil { 98 t.Fatalf("failed to receive TCP segment sent for RTT calculation: %s", err) 99 } 100 // Let the DUT estimate RTO with RTT from the DATA-ACK. 101 // TODO(gvisor.dev/issue/2685) Estimate RTO during handshake, after which 102 // we can skip sending this ACK. 103 conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}) 104 105 dut.Send(t, remoteFD, tc.payload, 0) 106 expectTCP := &testbench.TCP{SeqNum: testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))} 107 originalID, err := recvTCPSegment(t, &conn, expectTCP, samplePayload) 108 if err != nil { 109 t.Fatalf("failed to receive TCP segment: %s", err) 110 } 111 112 retransmitID, err := recvTCPSegment(t, &conn, expectTCP, samplePayload) 113 if err != nil { 114 t.Fatalf("failed to receive retransmitted TCP segment: %s", err) 115 } 116 if originalID == retransmitID { 117 t.Fatalf("unexpectedly got retransmitted TCP segment with same IPv4 ID field=%d", originalID) 118 } 119 }) 120 } 121 }