github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/hostinet/stack.go (about) 1 // Copyright 2018 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 hostinet 16 17 import ( 18 "fmt" 19 "io" 20 "io/ioutil" 21 "os" 22 "reflect" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/nicocha30/gvisor-ligolo/pkg/context" 28 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 29 "github.com/nicocha30/gvisor-ligolo/pkg/log" 30 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/inet" 31 "github.com/nicocha30/gvisor-ligolo/pkg/syserr" 32 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip" 33 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip/stack" 34 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 35 ) 36 37 var defaultRecvBufSize = inet.TCPBufferSize{ 38 Min: 4096, 39 Default: 87380, 40 Max: 6291456, 41 } 42 43 var defaultSendBufSize = inet.TCPBufferSize{ 44 Min: 4096, 45 Default: 16384, 46 Max: 4194304, 47 } 48 49 // Stack implements inet.Stack for host sockets. 50 type Stack struct { 51 // Stack is immutable. 52 supportsIPv6 bool 53 tcpRecovery inet.TCPLossRecovery 54 tcpRecvBufSize inet.TCPBufferSize 55 tcpSendBufSize inet.TCPBufferSize 56 tcpSACKEnabled bool 57 netDevFile *os.File 58 netSNMPFile *os.File 59 // allowedSocketTypes is the list of allowed socket types 60 allowedSocketTypes []AllowedSocketType 61 } 62 63 // Destroy implements inet.Stack.Destroy. 64 func (*Stack) Destroy() { 65 } 66 67 // NewStack returns an empty Stack containing no configuration. 68 func NewStack() *Stack { 69 return &Stack{} 70 } 71 72 // Configure sets up the stack using the current state of the host network. 73 func (s *Stack) Configure(allowRawSockets bool) error { 74 if _, err := os.Stat("/proc/net/if_inet6"); err == nil { 75 s.supportsIPv6 = true 76 } 77 78 s.tcpRecvBufSize = defaultRecvBufSize 79 if tcpRMem, err := readTCPBufferSizeFile("/proc/sys/net/ipv4/tcp_rmem"); err == nil { 80 s.tcpRecvBufSize = tcpRMem 81 } else { 82 log.Warningf("Failed to read TCP receive buffer size, using default values") 83 } 84 85 s.tcpSendBufSize = defaultSendBufSize 86 if tcpWMem, err := readTCPBufferSizeFile("/proc/sys/net/ipv4/tcp_wmem"); err == nil { 87 s.tcpSendBufSize = tcpWMem 88 } else { 89 log.Warningf("Failed to read TCP send buffer size, using default values") 90 } 91 92 // SACK is important for performance and even compatibility, assume it's 93 // enabled if we can't find the actual value. 94 s.tcpSACKEnabled = true 95 if sack, err := ioutil.ReadFile("/proc/sys/net/ipv4/tcp_sack"); err == nil { 96 s.tcpSACKEnabled = strings.TrimSpace(string(sack)) != "0" 97 } else { 98 log.Warningf("Failed to read if TCP SACK if enabled, setting to true") 99 } 100 101 if f, err := os.Open("/proc/net/dev"); err != nil { 102 log.Warningf("Failed to open /proc/net/dev: %v", err) 103 } else { 104 s.netDevFile = f 105 } 106 107 if f, err := os.Open("/proc/net/snmp"); err != nil { 108 log.Warningf("Failed to open /proc/net/snmp: %v", err) 109 } else { 110 s.netSNMPFile = f 111 } 112 113 s.allowedSocketTypes = AllowedSocketTypes 114 if allowRawSockets { 115 s.allowedSocketTypes = append(s.allowedSocketTypes, AllowedRawSocketTypes...) 116 } 117 118 return nil 119 } 120 121 func readTCPBufferSizeFile(filename string) (inet.TCPBufferSize, error) { 122 contents, err := ioutil.ReadFile(filename) 123 if err != nil { 124 return inet.TCPBufferSize{}, fmt.Errorf("failed to read %s: %v", filename, err) 125 } 126 ioseq := usermem.BytesIOSequence(contents) 127 fields := make([]int32, 3) 128 if n, err := usermem.CopyInt32StringsInVec(context.Background(), ioseq.IO, ioseq.Addrs, fields, ioseq.Opts); n != ioseq.NumBytes() || err != nil { 129 return inet.TCPBufferSize{}, fmt.Errorf("failed to parse %s (%q): got %v after %d/%d bytes", filename, contents, err, n, ioseq.NumBytes()) 130 } 131 return inet.TCPBufferSize{ 132 Min: int(fields[0]), 133 Default: int(fields[1]), 134 Max: int(fields[2]), 135 }, nil 136 } 137 138 // Interfaces implements inet.Stack.Interfaces. 139 func (s *Stack) Interfaces() map[int32]inet.Interface { 140 ifs, err := getInterfaces() 141 if err != nil { 142 log.Warningf("could not get host interface: %v", err) 143 return nil 144 } 145 146 // query interface features for each of the host interfaces. 147 if err := queryInterfaceFeatures(ifs); err != nil { 148 log.Warningf("could not query host interfaces: %v", err) 149 return nil 150 } 151 return ifs 152 } 153 154 // RemoveInterface implements inet.Stack.RemoveInterface. 155 func (*Stack) RemoveInterface(idx int32) error { 156 return removeInterface(idx) 157 } 158 159 // InterfaceAddrs implements inet.Stack.InterfaceAddrs. 160 func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr { 161 addrs, err := getInterfaceAddrs() 162 if err != nil { 163 log.Warningf("failed to get host interface addresses: %v", err) 164 return nil 165 } 166 return addrs 167 } 168 169 // AddInterfaceAddr implements inet.Stack.AddInterfaceAddr. 170 func (*Stack) AddInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { 171 return addInterfaceAddr(idx, addr) 172 } 173 174 // RemoveInterfaceAddr implements inet.Stack.RemoveInterfaceAddr. 175 func (*Stack) RemoveInterfaceAddr(idx int32, addr inet.InterfaceAddr) error { 176 return removeInterfaceAddr(idx, addr) 177 } 178 179 // SupportsIPv6 implements inet.Stack.SupportsIPv6. 180 func (s *Stack) SupportsIPv6() bool { 181 return s.supportsIPv6 182 } 183 184 // TCPReceiveBufferSize implements inet.Stack.TCPReceiveBufferSize. 185 func (s *Stack) TCPReceiveBufferSize() (inet.TCPBufferSize, error) { 186 return s.tcpRecvBufSize, nil 187 } 188 189 // SetTCPReceiveBufferSize implements inet.Stack.SetTCPReceiveBufferSize. 190 func (*Stack) SetTCPReceiveBufferSize(inet.TCPBufferSize) error { 191 return linuxerr.EACCES 192 } 193 194 // TCPSendBufferSize implements inet.Stack.TCPSendBufferSize. 195 func (s *Stack) TCPSendBufferSize() (inet.TCPBufferSize, error) { 196 return s.tcpSendBufSize, nil 197 } 198 199 // SetTCPSendBufferSize implements inet.Stack.SetTCPSendBufferSize. 200 func (*Stack) SetTCPSendBufferSize(inet.TCPBufferSize) error { 201 return linuxerr.EACCES 202 } 203 204 // TCPSACKEnabled implements inet.Stack.TCPSACKEnabled. 205 func (s *Stack) TCPSACKEnabled() (bool, error) { 206 return s.tcpSACKEnabled, nil 207 } 208 209 // SetTCPSACKEnabled implements inet.Stack.SetTCPSACKEnabled. 210 func (*Stack) SetTCPSACKEnabled(bool) error { 211 return linuxerr.EACCES 212 } 213 214 // TCPRecovery implements inet.Stack.TCPRecovery. 215 func (s *Stack) TCPRecovery() (inet.TCPLossRecovery, error) { 216 return s.tcpRecovery, nil 217 } 218 219 // SetTCPRecovery implements inet.Stack.SetTCPRecovery. 220 func (*Stack) SetTCPRecovery(inet.TCPLossRecovery) error { 221 return linuxerr.EACCES 222 } 223 224 // getLine reads one line from proc file, with specified prefix. 225 // The last argument, withHeader, specifies if it contains line header. 226 func getLine(f *os.File, prefix string, withHeader bool) string { 227 data := make([]byte, 4096) 228 229 if _, err := f.Seek(0, 0); err != nil { 230 return "" 231 } 232 233 if _, err := io.ReadFull(f, data); err != io.ErrUnexpectedEOF { 234 return "" 235 } 236 237 prefix = prefix + ":" 238 lines := strings.Split(string(data), "\n") 239 for _, l := range lines { 240 l = strings.TrimSpace(l) 241 if strings.HasPrefix(l, prefix) { 242 if withHeader { 243 withHeader = false 244 continue 245 } 246 return l 247 } 248 } 249 return "" 250 } 251 252 func toSlice(i any) []uint64 { 253 v := reflect.Indirect(reflect.ValueOf(i)) 254 return v.Slice(0, v.Len()).Interface().([]uint64) 255 } 256 257 // Statistics implements inet.Stack.Statistics. 258 func (s *Stack) Statistics(stat any, arg string) error { 259 var ( 260 snmpTCP bool 261 rawLine string 262 sliceStat []uint64 263 ) 264 265 switch stat.(type) { 266 case *inet.StatDev: 267 if s.netDevFile == nil { 268 return fmt.Errorf("/proc/net/dev is not opened for hostinet") 269 } 270 rawLine = getLine(s.netDevFile, arg, false /* with no header */) 271 case *inet.StatSNMPIP, *inet.StatSNMPICMP, *inet.StatSNMPICMPMSG, *inet.StatSNMPTCP, *inet.StatSNMPUDP, *inet.StatSNMPUDPLite: 272 if s.netSNMPFile == nil { 273 return fmt.Errorf("/proc/net/snmp is not opened for hostinet") 274 } 275 rawLine = getLine(s.netSNMPFile, arg, true) 276 default: 277 return syserr.ErrEndpointOperation.ToError() 278 } 279 280 if rawLine == "" { 281 return fmt.Errorf("failed to get raw line") 282 } 283 284 parts := strings.SplitN(rawLine, ":", 2) 285 if len(parts) != 2 { 286 return fmt.Errorf("failed to get prefix from: %q", rawLine) 287 } 288 289 sliceStat = toSlice(stat) 290 fields := strings.Fields(strings.TrimSpace(parts[1])) 291 if len(fields) != len(sliceStat) { 292 return fmt.Errorf("failed to parse fields: %q", rawLine) 293 } 294 if _, ok := stat.(*inet.StatSNMPTCP); ok { 295 snmpTCP = true 296 } 297 for i := 0; i < len(sliceStat); i++ { 298 var err error 299 if snmpTCP && i == 3 { 300 var tmp int64 301 // MaxConn field is signed, RFC 2012. 302 tmp, err = strconv.ParseInt(fields[i], 10, 64) 303 sliceStat[i] = uint64(tmp) // Convert back to int before use. 304 } else { 305 sliceStat[i], err = strconv.ParseUint(fields[i], 10, 64) 306 } 307 if err != nil { 308 return fmt.Errorf("failed to parse field %d from: %q, %v", i, rawLine, err) 309 } 310 } 311 312 return nil 313 } 314 315 // RouteTable implements inet.Stack.RouteTable. 316 func (s *Stack) RouteTable() []inet.Route { 317 routes, err := getRoutes() 318 if err != nil { 319 log.Warningf("failed to get routes: %v", err) 320 return nil 321 } 322 // Prepend empty route. 323 return append([]inet.Route(nil), routes...) 324 } 325 326 // Pause implements inet.Stack.Pause. 327 func (*Stack) Pause() {} 328 329 // Resume implements inet.Stack.Resume. 330 func (*Stack) Resume() {} 331 332 // RegisteredEndpoints implements inet.Stack.RegisteredEndpoints. 333 func (*Stack) RegisteredEndpoints() []stack.TransportEndpoint { return nil } 334 335 // CleanupEndpoints implements inet.Stack.CleanupEndpoints. 336 func (*Stack) CleanupEndpoints() []stack.TransportEndpoint { return nil } 337 338 // RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints. 339 func (*Stack) RestoreCleanupEndpoints([]stack.TransportEndpoint) {} 340 341 // SetForwarding implements inet.Stack.SetForwarding. 342 func (*Stack) SetForwarding(tcpip.NetworkProtocolNumber, bool) error { 343 return linuxerr.EACCES 344 } 345 346 // PortRange implements inet.Stack.PortRange. 347 func (*Stack) PortRange() (uint16, uint16) { 348 // Use the default Linux values per net/ipv4/af_inet.c:inet_init_net(). 349 return 32768, 60999 350 } 351 352 // SetPortRange implements inet.Stack.SetPortRange. 353 func (*Stack) SetPortRange(uint16, uint16) error { 354 return linuxerr.EACCES 355 } 356 357 // GROTimeout implements inet.Stack.GROTimeout. 358 func (s *Stack) GROTimeout(NICID int32) (time.Duration, error) { 359 return 0, nil 360 } 361 362 // SetGROTimeout implements inet.Stack.SetGROTimeout. 363 func (s *Stack) SetGROTimeout(NICID int32, timeout time.Duration) error { 364 // We don't support setting the hostinet GRO timeout. 365 return linuxerr.EINVAL 366 }