github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/pkg/socket/sockopts_posix.go (about) 1 // Copyright (c) 2023 Paweł Gaczyński 2 // Copyright (c) 2021 Andy Pan 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 package socket 17 18 import ( 19 "fmt" 20 "net" 21 "os" 22 "syscall" 23 24 "github.com/pawelgaczynski/gain/pkg/errors" 25 gainNet "github.com/pawelgaczynski/gain/pkg/net" 26 "golang.org/x/sys/unix" 27 ) 28 29 // SetNoDelay controls whether the operating system should delay 30 // packet transmission in hopes of sending fewer packets (Nagle's algorithm). 31 // 32 // The default is true (no delay), meaning that data is 33 // sent as soon as possible after a Write. 34 func SetNoDelay(fd, noDelay int) error { 35 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_NODELAY, noDelay)) 36 } 37 38 // SetRecvBuffer sets the size of the operating system's 39 // receive buffer associated with the connection. 40 func SetRecvBuffer(fd, size int) error { 41 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF, size)) 42 } 43 44 // SetSendBuffer sets the size of the operating system's 45 // transmit buffer associated with the connection. 46 func SetSendBuffer(fd, size int) error { 47 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_SNDBUF, size)) 48 } 49 50 // SetReuseport enables SO_REUSEPORT option on socket. 51 func SetReuseport(fd, reusePort int) error { 52 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, reusePort)) 53 } 54 55 // SetReuseAddr enables SO_REUSEADDR option on socket. 56 func SetReuseAddr(fd, reuseAddr int) error { 57 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, reuseAddr)) 58 } 59 60 // SetIPv6Only restricts a IPv6 socket to only process IPv6 requests or both IPv4 and IPv6 requests. 61 func SetIPv6Only(fd, ipv6only int) error { 62 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_V6ONLY, ipv6only)) 63 } 64 65 // SetQuickAck controls quickack mode on socket. 66 // If quickack mode, acks are sent immediately, rather than delayed if needed in accordance to normal TCP operation. 67 func SetQuickAck(fd, enabled int) error { 68 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, enabled)) 69 } 70 71 // SetFastOpen enables TCP_FASTOPEN on socket which allow to send and accept data in the opening SYN packet. 72 // https://sysctl-explorer.net/net/ipv4/tcp_fastopen/ 73 func SetFastOpen(fd, enabled int) error { 74 return os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_FASTOPEN, enabled)) 75 } 76 77 // SetLinger sets the behavior of Close on a connection which still 78 // has data waiting to be sent or to be acknowledged. 79 // 80 // If sec < 0 (the default), the operating system finishes sending the 81 // data in the background. 82 // 83 // If sec == 0, the operating system discards any unsent or 84 // unacknowledged data. 85 // 86 // If sec > 0, the data is sent in the background as with sec < 0. On 87 // some operating systems after sec seconds have elapsed any remaining 88 // unsent data may be discarded. 89 func SetLinger(fd, sec int) error { 90 var linger unix.Linger 91 if sec >= 0 { 92 linger.Onoff = 1 93 linger.Linger = int32(sec) 94 } else { 95 linger.Onoff = 0 96 linger.Linger = 0 97 } 98 99 return os.NewSyscallError("setsockopt", unix.SetsockoptLinger(fd, syscall.SOL_SOCKET, syscall.SO_LINGER, &linger)) 100 } 101 102 // SetMulticastMembership returns with a socket option function based on the IP 103 // version. Returns nil when multicast membership cannot be applied. 104 func SetMulticastMembership(proto string, udpAddr *net.UDPAddr) func(int, int) error { 105 udpVersion, err := determineUDPProto(proto, udpAddr) 106 if err != nil { 107 return nil 108 } 109 110 switch udpVersion { 111 case gainNet.UDP4: 112 return func(fd int, ifIndex int) error { 113 return SetIPv4MulticastMembership(fd, udpAddr.IP, ifIndex) 114 } 115 116 case gainNet.UDP6: 117 return func(fd int, ifIndex int) error { 118 return SetIPv6MulticastMembership(fd, udpAddr.IP, ifIndex) 119 } 120 121 default: 122 return nil 123 } 124 } 125 126 // SetIPv4MulticastMembership joins fd to the specified multicast IPv4 address. 127 // ifIndex is the index of the interface where the multicast datagrams will be 128 // received. If ifIndex is 0 then the operating system will choose the default, 129 // it is usually needed when the host has multiple network interfaces configured. 130 func SetIPv4MulticastMembership(fd int, mcast net.IP, ifIndex int) error { 131 // Multicast interfaces are selected by IP address on IPv4 (and by index on IPv6) 132 ip, err := interfaceFirstIPv4Addr(ifIndex) 133 if err != nil { 134 return err 135 } 136 137 mreq := &unix.IPMreq{} 138 copy(mreq.Multiaddr[:], mcast.To4()) 139 copy(mreq.Interface[:], ip.To4()) 140 141 if ifIndex > 0 { 142 if err = os.NewSyscallError( 143 "setsockopt", unix.SetsockoptInet4Addr(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq.Interface), 144 ); err != nil { 145 return err 146 } 147 } 148 149 if err = os.NewSyscallError( 150 "setsockopt", unix.SetsockoptByte(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 0), 151 ); err != nil { 152 return err 153 } 154 155 return os.NewSyscallError("setsockopt", unix.SetsockoptIPMreq(fd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) 156 } 157 158 // SetIPv6MulticastMembership joins fd to the specified multicast IPv6 address. 159 // ifIndex is the index of the interface where the multicast datagrams will be 160 // received. If ifIndex is 0 then the operating system will choose the default, 161 // it is usually needed when the host has multiple network interfaces configured. 162 func SetIPv6MulticastMembership(fd int, mcast net.IP, ifIndex int) error { 163 mreq := &unix.IPv6Mreq{} 164 mreq.Interface = uint32(ifIndex) 165 copy(mreq.Multiaddr[:], mcast.To16()) 166 167 if ifIndex > 0 { 168 if err := os.NewSyscallError( 169 "setsockopt", unix.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, ifIndex), 170 ); err != nil { 171 return err 172 } 173 } 174 175 if err := os.NewSyscallError( 176 "setsockopt", unix.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, 0), 177 ); err != nil { 178 return err 179 } 180 181 return os.NewSyscallError( 182 "setsockopt", unix.SetsockoptIPv6Mreq(fd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq), 183 ) 184 } 185 186 // interfaceFirstIPv4Addr returns the first IPv4 address of the interface. 187 func interfaceFirstIPv4Addr(ifIndex int) (net.IP, error) { 188 if ifIndex == 0 { 189 return net.IP([]byte{0, 0, 0, 0}), nil 190 } 191 192 iface, err := net.InterfaceByIndex(ifIndex) 193 if err != nil { 194 return nil, fmt.Errorf("interface by index error: %w", err) 195 } 196 197 addrs, err := iface.Addrs() 198 if err != nil { 199 return nil, fmt.Errorf("get addrs error: %w", err) 200 } 201 202 for _, addr := range addrs { 203 var ip net.IP 204 205 ip, _, err = net.ParseCIDR(addr.String()) 206 if err != nil { 207 return nil, fmt.Errorf("parse CIDR error: %w", err) 208 } 209 210 if ip.To4() != nil { 211 return ip, nil 212 } 213 } 214 215 return nil, errors.ErrNoIPv4AddressOnInterface 216 }