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  }