github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/http/dial_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 // Copyright (c) 2015-2023 MinIO, Inc. 5 // 6 // This file is part of MinIO Object Storage stack 7 // 8 // This program is free software: you can redistribute it and/or modify 9 // it under the terms of the GNU Affero General Public License as published by 10 // the Free Software Foundation, either version 3 of the License, or 11 // (at your option) any later version. 12 // 13 // This program is distributed in the hope that it will be useful 14 // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 // GNU Affero General Public License for more details. 17 // 18 // You should have received a copy of the GNU Affero General Public License 19 // along with this program. If not, see <http://www.gnu.org/licenses/>. 20 21 package http 22 23 import ( 24 "context" 25 "net" 26 "syscall" 27 "time" 28 29 "golang.org/x/sys/unix" 30 ) 31 32 func setTCPParametersFn(opts TCPOptions) func(network, address string, c syscall.RawConn) error { 33 return func(network, address string, c syscall.RawConn) error { 34 c.Control(func(fdPtr uintptr) { 35 // got socket file descriptor to set parameters. 36 fd := int(fdPtr) 37 38 _ = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) 39 40 _ = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) 41 42 // Enable TCP open 43 // https://lwn.net/Articles/508865/ - 16k queue size. 44 _ = syscall.SetsockoptInt(fd, syscall.SOL_TCP, unix.TCP_FASTOPEN, 16*1024) 45 46 // Enable TCP fast connect 47 // TCPFastOpenConnect sets the underlying socket to use 48 // the TCP fast open connect. This feature is supported 49 // since Linux 4.11. 50 _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, unix.TCP_FASTOPEN_CONNECT, 1) 51 52 // Enable TCP quick ACK, John Nagle says 53 // "Set TCP_QUICKACK. If you find a case where that makes things worse, let me know." 54 _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, unix.TCP_QUICKACK, 1) 55 56 // The time (in seconds) the connection needs to remain idle before 57 // TCP starts sending keepalive probes 58 _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, 15) 59 60 // Number of probes. 61 // ~ cat /proc/sys/net/ipv4/tcp_keepalive_probes (defaults to 9, we reduce it to 5) 62 _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, 5) 63 64 // Wait time after successful probe in seconds. 65 // ~ cat /proc/sys/net/ipv4/tcp_keepalive_intvl (defaults to 75 secs, we reduce it to 15 secs) 66 _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, 15) 67 68 // Set tcp user timeout in addition to the keep-alive - tcp-keepalive is not enough to close a socket 69 // with dead end because tcp-keepalive is not fired when there is data in the socket buffer. 70 // https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/ 71 // This is a sensitive configuration, it is better to set it to high values, > 60 secs since it can 72 // affect clients reading data with a very slow pace (disappropriate with socket buffer sizes) 73 _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, opts.UserTimeout) 74 75 if opts.Interface != "" { 76 if h, _, err := net.SplitHostPort(address); err == nil { 77 address = h 78 } 79 // Create socket on specific vrf device. 80 // To catch all kinds of special cases this filters specifically for loopback networks. 81 if ip := net.ParseIP(address); ip != nil && !ip.IsLoopback() { 82 _ = syscall.SetsockoptString(fd, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, opts.Interface) 83 } 84 } 85 }) 86 return nil 87 } 88 } 89 90 // DialContext is a function to make custom Dial for internode communications 91 type DialContext func(ctx context.Context, network, address string) (net.Conn, error) 92 93 // NewInternodeDialContext setups a custom dialer for internode communication 94 func NewInternodeDialContext(dialTimeout time.Duration, opts TCPOptions) DialContext { 95 return func(ctx context.Context, network, addr string) (net.Conn, error) { 96 dialer := &net.Dialer{ 97 Timeout: dialTimeout, 98 Control: setTCPParametersFn(opts), 99 } 100 return dialer.DialContext(ctx, network, addr) 101 } 102 } 103 104 // NewCustomDialContext setups a custom dialer for any external communication and proxies. 105 func NewCustomDialContext(dialTimeout time.Duration, opts TCPOptions) DialContext { 106 return func(ctx context.Context, network, addr string) (net.Conn, error) { 107 dialer := &net.Dialer{ 108 Timeout: dialTimeout, 109 Control: setTCPParametersFn(opts), 110 } 111 return dialer.DialContext(ctx, network, addr) 112 } 113 }