k8s.io/kubernetes@v1.29.3/test/images/agnhost/net/nat/closewait.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package nat 18 19 /* 20 client/server for testing CLOSE_WAIT timeout condition in iptables NAT. 21 22 client server 23 | | 24 |<--tcp handshake-->| 25 |<-------fin--------| half-close from server 26 | | client is in CLOSE_WAIT 27 */ 28 29 import ( 30 "errors" 31 "io" 32 "log" 33 "net" 34 "time" 35 36 "k8s.io/kubernetes/test/images/agnhost/net/common" 37 ) 38 39 // leakedConnection is a global variable that should leak the active 40 // connection assigned here. 41 // 42 //nolint:unused // U1000 intentional unused variable 43 var leakedConnection *net.TCPConn 44 45 // CloseWaitServerOptions holds server JSON options. 46 type CloseWaitServerOptions struct { 47 // Address to bind for the test 48 LocalAddr string 49 // Timeout to wait after sending the FIN. 50 PostFinTimeoutSeconds int 51 } 52 53 type closeWaitServer struct { 54 options *CloseWaitServerOptions 55 } 56 57 // NewCloseWaitServer returns a new Runner. 58 func NewCloseWaitServer() common.Runner { 59 return &closeWaitServer{} 60 } 61 62 // NewOptions allocates new options structure. 63 func (server *closeWaitServer) NewOptions() interface{} { 64 return &CloseWaitServerOptions{} 65 } 66 67 // Run the server-side of the test. 68 func (server *closeWaitServer) Run(logger *log.Logger, rawOptions interface{}) error { 69 if options, ok := rawOptions.(*CloseWaitServerOptions); ok { 70 server.options = options 71 } else { 72 return errors.New("invalid type") 73 } 74 75 logger.Printf("Run %v", server.options) 76 77 addr, err := net.ResolveTCPAddr("tcp", server.options.LocalAddr) 78 if err != nil { 79 return err 80 } 81 82 listener, err := net.ListenTCP("tcp", addr) 83 if err != nil { 84 return err 85 } 86 defer listener.Close() 87 88 logger.Printf("Server listening on %v", addr) 89 90 conn, err := listener.AcceptTCP() 91 if err != nil { 92 return err 93 } 94 defer conn.Close() 95 96 logger.Printf("Client connected") 97 98 // Send client half-close FIN so client is now in CLOSE_WAIT. We keep 99 // the client -> server pipe open to verify whether or not the NAT 100 // dropped our connection. 101 if err := conn.CloseWrite(); err != nil { 102 return err 103 } 104 105 logger.Printf("Server sent FIN, waiting %v seconds", 106 server.options.PostFinTimeoutSeconds) 107 108 <-time.After(time.Duration(server.options.PostFinTimeoutSeconds) * time.Second) 109 110 logger.Printf("Done") 111 112 return nil 113 } 114 115 // CloseWaitClientOptions holds client JSON options. 116 type CloseWaitClientOptions struct { 117 // RemoteAddr of the server to connect to. 118 RemoteAddr string 119 // TimeoutSeconds on I/O with the server. 120 TimeoutSeconds int 121 // Half-close timeout (to give the test time to check the status of the 122 // conntrack table entry. 123 PostFinTimeoutSeconds int 124 // Leak connection (assign to global variable so connection persists 125 // as long as the process remains. 126 LeakConnection bool 127 } 128 129 type closeWaitClient struct { 130 options *CloseWaitClientOptions 131 } 132 133 // NewCloseWaitClient creates a new runner 134 func NewCloseWaitClient() common.Runner { 135 return &closeWaitClient{} 136 } 137 138 // NewOptions allocates new options structure. 139 func (client *closeWaitClient) NewOptions() interface{} { 140 return &CloseWaitClientOptions{} 141 } 142 143 // Run the client.m 144 func (client *closeWaitClient) Run(logger *log.Logger, rawOptions interface{}) error { 145 if options, ok := rawOptions.(*CloseWaitClientOptions); ok { 146 client.options = options 147 } else { 148 return errors.New("invalid type") 149 } 150 151 logger.Printf("Run %v", client.options) 152 153 addr, err := net.ResolveTCPAddr("tcp", client.options.RemoteAddr) 154 if err != nil { 155 return err 156 } 157 158 conn, err := net.DialTCP("tcp", nil, addr) 159 if err != nil { 160 return err 161 } 162 if !client.options.LeakConnection { 163 defer conn.Close() 164 } 165 166 logger.Printf("Connected to server") 167 168 if client.options.TimeoutSeconds > 0 { 169 delay := time.Duration(client.options.TimeoutSeconds) * time.Second 170 conn.SetReadDeadline(time.Now().Add(delay)) 171 } 172 173 buf := make([]byte, 1, 1) 174 size, err := conn.Read(buf) 175 176 if err != nil && err != io.EOF { 177 return err 178 } 179 180 if size != 0 { 181 return errors.New("Got data but expected EOF") 182 } 183 184 logger.Printf("Server has half-closed the connection, waiting %v seconds", 185 client.options.PostFinTimeoutSeconds) 186 187 if client.options.LeakConnection { 188 logger.Printf("Leaking client connection (assigning to global variable)") 189 leakedConnection = conn 190 } 191 192 <-time.After( 193 time.Duration(client.options.PostFinTimeoutSeconds) * time.Second) 194 195 logger.Printf("Done") 196 197 return nil 198 }