hostinet: support /proc/net/snmp and /proc/net/dev
For hostinet, we inherit the data from host procfs. To to that, we cache the fds for these files for later reads. Fixes #506 Signed-off-by: Jianfeng Tan <henry.tjf@antfin.com> Change-Id: I2f81215477455b9c59acf67e33f5b9af28ee0165
This commit is contained in:
parent
b94505ecc0
commit
dd7d1f825d
|
@ -16,8 +16,11 @@ package hostinet
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
|
@ -26,6 +29,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
|
@ -51,6 +55,8 @@ type Stack struct {
|
|||
tcpRecvBufSize inet.TCPBufferSize
|
||||
tcpSendBufSize inet.TCPBufferSize
|
||||
tcpSACKEnabled bool
|
||||
netDevFile *os.File
|
||||
netSNMPFile *os.File
|
||||
}
|
||||
|
||||
// NewStack returns an empty Stack containing no configuration.
|
||||
|
@ -98,6 +104,18 @@ func (s *Stack) Configure() error {
|
|||
log.Warningf("Failed to read if TCP SACK if enabled, setting to true")
|
||||
}
|
||||
|
||||
if f, err := os.Open("/proc/net/dev"); err != nil {
|
||||
log.Warningf("Failed to open /proc/net/dev: %v", err)
|
||||
} else {
|
||||
s.netDevFile = f
|
||||
}
|
||||
|
||||
if f, err := os.Open("/proc/net/snmp"); err != nil {
|
||||
log.Warningf("Failed to open /proc/net/snmp: %v", err)
|
||||
} else {
|
||||
s.netSNMPFile = f
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -326,9 +344,95 @@ func (s *Stack) SetTCPSACKEnabled(enabled bool) error {
|
|||
return syserror.EACCES
|
||||
}
|
||||
|
||||
// getLine reads one line from proc file, with specified prefix.
|
||||
// The last argument, withHeader, specifies if it contains line header.
|
||||
func getLine(f *os.File, prefix string, withHeader bool) string {
|
||||
data := make([]byte, 4096)
|
||||
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(f, data); err != io.ErrUnexpectedEOF {
|
||||
return ""
|
||||
}
|
||||
|
||||
prefix = prefix + ":"
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, l := range lines {
|
||||
l = strings.TrimSpace(l)
|
||||
if strings.HasPrefix(l, prefix) {
|
||||
if withHeader {
|
||||
withHeader = false
|
||||
continue
|
||||
}
|
||||
return l
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toSlice(i interface{}) []uint64 {
|
||||
v := reflect.Indirect(reflect.ValueOf(i))
|
||||
return v.Slice(0, v.Len()).Interface().([]uint64)
|
||||
}
|
||||
|
||||
// Statistics implements inet.Stack.Statistics.
|
||||
func (s *Stack) Statistics(stat interface{}, arg string) error {
|
||||
return syserror.EOPNOTSUPP
|
||||
var (
|
||||
snmpTCP bool
|
||||
rawLine string
|
||||
sliceStat []uint64
|
||||
)
|
||||
|
||||
switch stat.(type) {
|
||||
case *inet.StatDev:
|
||||
if s.netDevFile == nil {
|
||||
return fmt.Errorf("/proc/net/dev is not opened for hostinet")
|
||||
}
|
||||
rawLine = getLine(s.netDevFile, arg, false /* with no header */)
|
||||
case *inet.StatSNMPIP, *inet.StatSNMPICMP, *inet.StatSNMPICMPMSG, *inet.StatSNMPTCP, *inet.StatSNMPUDP, *inet.StatSNMPUDPLite:
|
||||
if s.netSNMPFile == nil {
|
||||
return fmt.Errorf("/proc/net/snmp is not opened for hostinet")
|
||||
}
|
||||
rawLine = getLine(s.netSNMPFile, arg, true)
|
||||
default:
|
||||
return syserr.ErrEndpointOperation.ToError()
|
||||
}
|
||||
|
||||
if rawLine == "" {
|
||||
return fmt.Errorf("Failed to get raw line")
|
||||
}
|
||||
|
||||
parts := strings.SplitN(rawLine, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("Failed to get prefix from: %q", rawLine)
|
||||
}
|
||||
|
||||
sliceStat = toSlice(stat)
|
||||
fields := strings.Fields(strings.TrimSpace(parts[1]))
|
||||
if len(fields) != len(sliceStat) {
|
||||
return fmt.Errorf("Failed to parse fields: %q", rawLine)
|
||||
}
|
||||
if _, ok := stat.(*inet.StatSNMPTCP); ok {
|
||||
snmpTCP = true
|
||||
}
|
||||
for i := 0; i < len(sliceStat); i++ {
|
||||
var err error
|
||||
if snmpTCP && i == 3 {
|
||||
var tmp int64
|
||||
// MaxConn field is signed, RFC 2012.
|
||||
tmp, err = strconv.ParseInt(fields[i], 10, 64)
|
||||
sliceStat[i] = uint64(tmp) // Convert back to int before use.
|
||||
} else {
|
||||
sliceStat[i], err = strconv.ParseUint(fields[i], 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse field %d from: %q, %v", i, rawLine, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RouteTable implements inet.Stack.RouteTable.
|
||||
|
|
Loading…
Reference in New Issue