gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/proc/task_net.go (about)

     1  // Copyright 2019 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 proc
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"reflect"
    22  	"time"
    23  
    24  	"gvisor.dev/gvisor/pkg/abi/linux"
    25  	"gvisor.dev/gvisor/pkg/context"
    26  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    27  	"gvisor.dev/gvisor/pkg/hostarch"
    28  	"gvisor.dev/gvisor/pkg/log"
    29  	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
    30  	"gvisor.dev/gvisor/pkg/sentry/inet"
    31  	"gvisor.dev/gvisor/pkg/sentry/kernel"
    32  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    33  	"gvisor.dev/gvisor/pkg/sentry/socket"
    34  	"gvisor.dev/gvisor/pkg/sentry/socket/unix"
    35  	"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
    36  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    37  	"gvisor.dev/gvisor/pkg/tcpip/header"
    38  )
    39  
    40  func (fs *filesystem) newTaskNetDir(ctx context.Context, task *kernel.Task) kernfs.Inode {
    41  	k := task.Kernel()
    42  	pidns := task.PIDNamespace()
    43  	root := auth.NewRootCredentials(pidns.UserNamespace())
    44  
    45  	var contents map[string]kernfs.Inode
    46  	var stack inet.Stack
    47  	if netns := task.GetNetworkNamespace(); netns != nil {
    48  		netns.DecRef(ctx)
    49  		stack = netns.Stack()
    50  	}
    51  	if stack != nil {
    52  		const (
    53  			arp       = "IP address       HW type     Flags       HW address            Mask     Device\n"
    54  			netlink   = "sk       Eth Pid    Groups   Rmem     Wmem     Dump     Locks     Drops     Inode\n"
    55  			packet    = "sk       RefCnt Type Proto  Iface R Rmem   User   Inode\n"
    56  			protocols = "protocol  size sockets  memory press maxhdr  slab module     cl co di ac io in de sh ss gs se re sp bi br ha uh gp em\n"
    57  			ptype     = "Type Device      Function\n"
    58  			upd6      = "  sl  local_address                         remote_address                        st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode\n"
    59  		)
    60  		psched := fmt.Sprintf("%08x %08x %08x %08x\n", uint64(time.Microsecond/time.Nanosecond), 64, 1000000, uint64(time.Second/time.Nanosecond))
    61  
    62  		// TODO(gvisor.dev/issue/1833): Make sure file contents reflect the task
    63  		// network namespace.
    64  		contents = map[string]kernfs.Inode{
    65  			"dev":  fs.newInode(ctx, root, 0444, &netDevData{stack: stack}),
    66  			"snmp": fs.newInode(ctx, root, 0444, &netSnmpData{stack: stack}),
    67  
    68  			// The following files are simple stubs until they are implemented in
    69  			// netstack, if the file contains a header the stub is just the header
    70  			// otherwise it is an empty file.
    71  			"arp":       fs.newInode(ctx, root, 0444, newStaticFile(arp)),
    72  			"netlink":   fs.newInode(ctx, root, 0444, newStaticFile(netlink)),
    73  			"netstat":   fs.newInode(ctx, root, 0444, &netStatData{}),
    74  			"packet":    fs.newInode(ctx, root, 0444, newStaticFile(packet)),
    75  			"protocols": fs.newInode(ctx, root, 0444, newStaticFile(protocols)),
    76  
    77  			// Linux sets psched values to: nsec per usec, psched tick in ns, 1000000,
    78  			// high res timer ticks per sec (ClockGetres returns 1ns resolution).
    79  			"psched": fs.newInode(ctx, root, 0444, newStaticFile(psched)),
    80  			"ptype":  fs.newInode(ctx, root, 0444, newStaticFile(ptype)),
    81  			"route":  fs.newInode(ctx, root, 0444, &netRouteData{stack: stack}),
    82  			"tcp":    fs.newInode(ctx, root, 0444, &netTCPData{kernel: k}),
    83  			"udp":    fs.newInode(ctx, root, 0444, &netUDPData{kernel: k}),
    84  			"unix":   fs.newInode(ctx, root, 0444, &netUnixData{kernel: k}),
    85  		}
    86  
    87  		if stack.SupportsIPv6() {
    88  			contents["if_inet6"] = fs.newInode(ctx, root, 0444, &ifinet6{stack: stack})
    89  			contents["ipv6_route"] = fs.newInode(ctx, root, 0444, newStaticFile(""))
    90  			contents["tcp6"] = fs.newInode(ctx, root, 0444, &netTCP6Data{kernel: k})
    91  			contents["udp6"] = fs.newInode(ctx, root, 0444, newStaticFile(upd6))
    92  		}
    93  	}
    94  
    95  	return fs.newTaskOwnedDir(ctx, task, fs.NextIno(), 0555, contents)
    96  }
    97  
    98  // ifinet6 implements vfs.DynamicBytesSource for /proc/net/if_inet6.
    99  //
   100  // +stateify savable
   101  type ifinet6 struct {
   102  	kernfs.DynamicBytesFile
   103  
   104  	stack inet.Stack
   105  }
   106  
   107  var _ dynamicInode = (*ifinet6)(nil)
   108  
   109  func (n *ifinet6) contents() []string {
   110  	var lines []string
   111  	nics := n.stack.Interfaces()
   112  	for id, naddrs := range n.stack.InterfaceAddrs() {
   113  		nic, ok := nics[id]
   114  		if !ok {
   115  			// NIC was added after NICNames was called. We'll just ignore it.
   116  			continue
   117  		}
   118  
   119  		for _, a := range naddrs {
   120  			// IPv6 only.
   121  			if a.Family != linux.AF_INET6 {
   122  				continue
   123  			}
   124  
   125  			// Fields:
   126  			// IPv6 address displayed in 32 hexadecimal chars without colons
   127  			// Netlink device number (interface index) in hexadecimal (use nic id)
   128  			// Prefix length in hexadecimal
   129  			// Scope value (use 0)
   130  			// Interface flags
   131  			// Device name
   132  			lines = append(lines, fmt.Sprintf("%032x %02x %02x %02x %02x %8s\n", a.Addr, id, a.PrefixLen, 0, a.Flags, nic.Name))
   133  		}
   134  	}
   135  	return lines
   136  }
   137  
   138  // Generate implements vfs.DynamicBytesSource.Generate.
   139  func (n *ifinet6) Generate(ctx context.Context, buf *bytes.Buffer) error {
   140  	for _, l := range n.contents() {
   141  		buf.WriteString(l)
   142  	}
   143  	return nil
   144  }
   145  
   146  // netDevData implements vfs.DynamicBytesSource for /proc/net/dev.
   147  //
   148  // +stateify savable
   149  type netDevData struct {
   150  	kernfs.DynamicBytesFile
   151  
   152  	stack inet.Stack
   153  }
   154  
   155  var _ dynamicInode = (*netDevData)(nil)
   156  
   157  // Generate implements vfs.DynamicBytesSource.Generate.
   158  func (n *netDevData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   159  	interfaces := n.stack.Interfaces()
   160  	buf.WriteString("Inter-|   Receive                                                |  Transmit\n")
   161  	buf.WriteString(" face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n")
   162  
   163  	for _, i := range interfaces {
   164  		// Implements the same format as
   165  		// net/core/net-procfs.c:dev_seq_printf_stats.
   166  		var stats inet.StatDev
   167  		if err := n.stack.Statistics(&stats, i.Name); err != nil {
   168  			log.Warningf("Failed to retrieve interface statistics for %v: %v", i.Name, err)
   169  			continue
   170  		}
   171  		fmt.Fprintf(
   172  			buf,
   173  			"%6s: %7d %7d %4d %4d %4d %5d %10d %9d %8d %7d %4d %4d %4d %5d %7d %10d\n",
   174  			i.Name,
   175  			// Received
   176  			stats[0], // bytes
   177  			stats[1], // packets
   178  			stats[2], // errors
   179  			stats[3], // dropped
   180  			stats[4], // fifo
   181  			stats[5], // frame
   182  			stats[6], // compressed
   183  			stats[7], // multicast
   184  			// Transmitted
   185  			stats[8],  // bytes
   186  			stats[9],  // packets
   187  			stats[10], // errors
   188  			stats[11], // dropped
   189  			stats[12], // fifo
   190  			stats[13], // frame
   191  			stats[14], // compressed
   192  			stats[15], // multicast
   193  		)
   194  	}
   195  
   196  	return nil
   197  }
   198  
   199  // netUnixData implements vfs.DynamicBytesSource for /proc/net/unix.
   200  //
   201  // +stateify savable
   202  type netUnixData struct {
   203  	kernfs.DynamicBytesFile
   204  
   205  	kernel *kernel.Kernel
   206  }
   207  
   208  var _ dynamicInode = (*netUnixData)(nil)
   209  
   210  // Generate implements vfs.DynamicBytesSource.Generate.
   211  func (n *netUnixData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   212  	buf.WriteString("Num       RefCount Protocol Flags    Type St Inode Path\n")
   213  	for _, se := range n.kernel.ListSockets() {
   214  		s := se.Sock
   215  		if !s.TryIncRef() {
   216  			// Racing with socket destruction, this is ok.
   217  			continue
   218  		}
   219  		if family, _, _ := s.Impl().(socket.Socket).Type(); family != linux.AF_UNIX {
   220  			s.DecRef(ctx)
   221  			// Not a unix socket.
   222  			continue
   223  		}
   224  		sops := s.Impl().(*unix.Socket)
   225  
   226  		addr, err := sops.Endpoint().GetLocalAddress()
   227  		if err != nil {
   228  			log.Warningf("Failed to retrieve socket name from %+v: %v", s, err)
   229  			addr.Addr = "<unknown>"
   230  		}
   231  
   232  		sockFlags := 0
   233  		if ce, ok := sops.Endpoint().(transport.ConnectingEndpoint); ok {
   234  			ce.Lock()
   235  			if ce.ListeningLocked() {
   236  				// For unix domain sockets, linux reports a single flag
   237  				// value if the socket is listening, of __SO_ACCEPTCON.
   238  				sockFlags = linux.SO_ACCEPTCON
   239  			}
   240  			ce.Unlock()
   241  		}
   242  
   243  		// Get inode number.
   244  		var ino uint64
   245  		stat, statErr := s.Stat(ctx, vfs.StatOptions{Mask: linux.STATX_INO})
   246  		if statErr != nil || stat.Mask&linux.STATX_INO == 0 {
   247  			log.Warningf("Failed to retrieve ino for socket file: %v", statErr)
   248  		} else {
   249  			ino = stat.Ino
   250  		}
   251  
   252  		// In the socket entry below, the value for the 'Num' field requires
   253  		// some consideration. Linux prints the address to the struct
   254  		// unix_sock representing a socket in the kernel, but may redact the
   255  		// value for unprivileged users depending on the kptr_restrict
   256  		// sysctl.
   257  		//
   258  		// One use for this field is to allow a privileged user to
   259  		// introspect into the kernel memory to determine information about
   260  		// a socket not available through procfs, such as the socket's peer.
   261  		//
   262  		// In gvisor, returning a pointer to our internal structures would
   263  		// be pointless, as it wouldn't match the memory layout for struct
   264  		// unix_sock, making introspection difficult. We could populate a
   265  		// struct unix_sock with the appropriate data, but even that
   266  		// requires consideration for which kernel version to emulate, as
   267  		// the definition of this struct changes over time.
   268  		//
   269  		// For now, we always redact this pointer.
   270  		fmt.Fprintf(buf, "%#016p: %08X %08X %08X %04X %02X %8d",
   271  			(*unix.Socket)(nil),    // Num, pointer to kernel socket struct.
   272  			s.ReadRefs()-1,         // RefCount, don't count our own ref.
   273  			0,                      // Protocol, always 0 for UDS.
   274  			sockFlags,              // Flags.
   275  			sops.Endpoint().Type(), // Type.
   276  			sops.State(),           // State.
   277  			ino,                    // Inode.
   278  		)
   279  
   280  		// Path
   281  		if len(addr.Addr) != 0 {
   282  			if addr.Addr[0] == 0 {
   283  				// Abstract path.
   284  				fmt.Fprintf(buf, " @%s", string(addr.Addr[1:]))
   285  			} else {
   286  				fmt.Fprintf(buf, " %s", string(addr.Addr))
   287  			}
   288  		}
   289  		fmt.Fprintf(buf, "\n")
   290  
   291  		s.DecRef(ctx)
   292  	}
   293  	return nil
   294  }
   295  
   296  func networkToHost16(n uint16) uint16 {
   297  	// n is in network byte order, so is big-endian. The most-significant byte
   298  	// should be stored in the lower address.
   299  	//
   300  	// We manually inline binary.BigEndian.Uint16() because Go does not support
   301  	// non-primitive consts, so binary.BigEndian is a (mutable) var, so calls to
   302  	// binary.BigEndian.Uint16() require a read of binary.BigEndian and an
   303  	// interface method call, defeating inlining.
   304  	buf := [2]byte{byte(n >> 8 & 0xff), byte(n & 0xff)}
   305  	return hostarch.ByteOrder.Uint16(buf[:])
   306  }
   307  
   308  func writeInetAddr(w io.Writer, family int, i linux.SockAddr) {
   309  	switch family {
   310  	case linux.AF_INET:
   311  		var a linux.SockAddrInet
   312  		if i != nil {
   313  			a = *i.(*linux.SockAddrInet)
   314  		}
   315  
   316  		// linux.SockAddrInet.Port is stored in the network byte order and is
   317  		// printed like a number in host byte order. Note that all numbers in host
   318  		// byte order are printed with the most-significant byte first when
   319  		// formatted with %X. See get_tcp4_sock() and udp4_format_sock() in Linux.
   320  		port := networkToHost16(a.Port)
   321  
   322  		// linux.SockAddrInet.Addr is stored as a byte slice in big-endian order
   323  		// (i.e. most-significant byte in index 0). Linux represents this as a
   324  		// __be32 which is a typedef for an unsigned int, and is printed with
   325  		// %X. This means that for a little-endian machine, Linux prints the
   326  		// least-significant byte of the address first. To emulate this, we first
   327  		// invert the byte order for the address using hostarch.ByteOrder.Uint32,
   328  		// which makes it have the equivalent encoding to a __be32 on a little
   329  		// endian machine. Note that this operation is a no-op on a big endian
   330  		// machine. Then similar to Linux, we format it with %X, which will print
   331  		// the most-significant byte of the __be32 address first, which is now
   332  		// actually the least-significant byte of the original address in
   333  		// linux.SockAddrInet.Addr on little endian machines, due to the conversion.
   334  		addr := hostarch.ByteOrder.Uint32(a.Addr[:])
   335  
   336  		fmt.Fprintf(w, "%08X:%04X ", addr, port)
   337  	case linux.AF_INET6:
   338  		var a linux.SockAddrInet6
   339  		if i != nil {
   340  			a = *i.(*linux.SockAddrInet6)
   341  		}
   342  
   343  		port := networkToHost16(a.Port)
   344  		addr0 := hostarch.ByteOrder.Uint32(a.Addr[0:4])
   345  		addr1 := hostarch.ByteOrder.Uint32(a.Addr[4:8])
   346  		addr2 := hostarch.ByteOrder.Uint32(a.Addr[8:12])
   347  		addr3 := hostarch.ByteOrder.Uint32(a.Addr[12:16])
   348  		fmt.Fprintf(w, "%08X%08X%08X%08X:%04X ", addr0, addr1, addr2, addr3, port)
   349  	}
   350  }
   351  
   352  func commonGenerateTCP(ctx context.Context, buf *bytes.Buffer, k *kernel.Kernel, family int) error {
   353  	// t may be nil here if our caller is not part of a task goroutine. This can
   354  	// happen for example if we're here for "sentryctl cat". When t is nil,
   355  	// degrade gracefully and retrieve what we can.
   356  	t := kernel.TaskFromContext(ctx)
   357  
   358  	for _, se := range k.ListSockets() {
   359  		s := se.Sock
   360  		if !s.TryIncRef() {
   361  			// Racing with socket destruction, this is ok.
   362  			continue
   363  		}
   364  		sops, ok := s.Impl().(socket.Socket)
   365  		if !ok {
   366  			panic(fmt.Sprintf("Found non-socket file in socket table: %+v", s))
   367  		}
   368  		if fa, stype, _ := sops.Type(); !(family == fa && stype == linux.SOCK_STREAM) {
   369  			s.DecRef(ctx)
   370  			// Not tcp4 sockets.
   371  			continue
   372  		}
   373  
   374  		// Linux's documentation for the fields below can be found at
   375  		// https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt.
   376  		// For Linux's implementation, see net/ipv4/tcp_ipv4.c:get_tcp4_sock().
   377  		// Note that the header doesn't contain labels for all the fields.
   378  
   379  		// Field: sl; entry number.
   380  		fmt.Fprintf(buf, "%4d: ", se.ID)
   381  
   382  		// Field: local_adddress.
   383  		var localAddr linux.SockAddr
   384  		if t != nil {
   385  			if local, _, err := sops.GetSockName(t); err == nil {
   386  				localAddr = local
   387  			}
   388  		}
   389  		writeInetAddr(buf, family, localAddr)
   390  
   391  		// Field: rem_address.
   392  		var remoteAddr linux.SockAddr
   393  		if t != nil {
   394  			if remote, _, err := sops.GetPeerName(t); err == nil {
   395  				remoteAddr = remote
   396  			}
   397  		}
   398  		writeInetAddr(buf, family, remoteAddr)
   399  
   400  		// Field: state; socket state.
   401  		fmt.Fprintf(buf, "%02X ", sops.State())
   402  
   403  		// Field: tx_queue, rx_queue; number of packets in the transmit and
   404  		// receive queue. Unimplemented.
   405  		fmt.Fprintf(buf, "%08X:%08X ", 0, 0)
   406  
   407  		// Field: tr, tm->when; timer active state and number of jiffies
   408  		// until timer expires. Unimplemented.
   409  		fmt.Fprintf(buf, "%02X:%08X ", 0, 0)
   410  
   411  		// Field: retrnsmt; number of unrecovered RTO timeouts.
   412  		// Unimplemented.
   413  		fmt.Fprintf(buf, "%08X ", 0)
   414  
   415  		stat, statErr := s.Stat(ctx, vfs.StatOptions{Mask: linux.STATX_UID | linux.STATX_INO})
   416  
   417  		// Field: uid.
   418  		if statErr != nil || stat.Mask&linux.STATX_UID == 0 {
   419  			log.Warningf("Failed to retrieve uid for socket file: %v", statErr)
   420  			fmt.Fprintf(buf, "%5d ", 0)
   421  		} else {
   422  			creds := auth.CredentialsFromContext(ctx)
   423  			fmt.Fprintf(buf, "%5d ", uint32(auth.KUID(stat.UID).In(creds.UserNamespace).OrOverflow()))
   424  		}
   425  
   426  		// Field: timeout; number of unanswered 0-window probes.
   427  		// Unimplemented.
   428  		fmt.Fprintf(buf, "%8d ", 0)
   429  
   430  		// Field: inode.
   431  		if statErr != nil || stat.Mask&linux.STATX_INO == 0 {
   432  			log.Warningf("Failed to retrieve inode for socket file: %v", statErr)
   433  			fmt.Fprintf(buf, "%8d ", 0)
   434  		} else {
   435  			fmt.Fprintf(buf, "%8d ", stat.Ino)
   436  		}
   437  
   438  		// Field: refcount. Don't count the ref we obtain while dereferencing
   439  		// the weakref to this socket.
   440  		fmt.Fprintf(buf, "%d ", s.ReadRefs()-1)
   441  
   442  		// Field: Socket struct address. Redacted due to the same reason as
   443  		// the 'Num' field in /proc/net/unix, see netUnix.ReadSeqFileData.
   444  		fmt.Fprintf(buf, "%#016p ", (*socket.Socket)(nil))
   445  
   446  		// Field: retransmit timeout. Unimplemented.
   447  		fmt.Fprintf(buf, "%d ", 0)
   448  
   449  		// Field: predicted tick of soft clock (delayed ACK control data).
   450  		// Unimplemented.
   451  		fmt.Fprintf(buf, "%d ", 0)
   452  
   453  		// Field: (ack.quick<<1)|ack.pingpong, Unimplemented.
   454  		fmt.Fprintf(buf, "%d ", 0)
   455  
   456  		// Field: sending congestion window, Unimplemented.
   457  		fmt.Fprintf(buf, "%d ", 0)
   458  
   459  		// Field: Slow start size threshold, -1 if threshold >= 0xFFFF.
   460  		// Unimplemented, report as large threshold.
   461  		fmt.Fprintf(buf, "%d", -1)
   462  
   463  		fmt.Fprintf(buf, "\n")
   464  
   465  		s.DecRef(ctx)
   466  	}
   467  
   468  	return nil
   469  }
   470  
   471  // netTCPData implements vfs.DynamicBytesSource for /proc/net/tcp.
   472  //
   473  // +stateify savable
   474  type netTCPData struct {
   475  	kernfs.DynamicBytesFile
   476  
   477  	kernel *kernel.Kernel
   478  }
   479  
   480  var _ dynamicInode = (*netTCPData)(nil)
   481  
   482  func (d *netTCPData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   483  	buf.WriteString("  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     \n")
   484  	return commonGenerateTCP(ctx, buf, d.kernel, linux.AF_INET)
   485  }
   486  
   487  // netTCP6Data implements vfs.DynamicBytesSource for /proc/net/tcp6.
   488  //
   489  // +stateify savable
   490  type netTCP6Data struct {
   491  	kernfs.DynamicBytesFile
   492  
   493  	kernel *kernel.Kernel
   494  }
   495  
   496  var _ dynamicInode = (*netTCP6Data)(nil)
   497  
   498  func (d *netTCP6Data) Generate(ctx context.Context, buf *bytes.Buffer) error {
   499  	buf.WriteString("  sl  local_address                         remote_address                        st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode\n")
   500  	return commonGenerateTCP(ctx, buf, d.kernel, linux.AF_INET6)
   501  }
   502  
   503  // netUDPData implements vfs.DynamicBytesSource for /proc/net/udp.
   504  //
   505  // +stateify savable
   506  type netUDPData struct {
   507  	kernfs.DynamicBytesFile
   508  
   509  	kernel *kernel.Kernel
   510  }
   511  
   512  var _ dynamicInode = (*netUDPData)(nil)
   513  
   514  // Generate implements vfs.DynamicBytesSource.Generate.
   515  func (d *netUDPData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   516  	// t may be nil here if our caller is not part of a task goroutine. This can
   517  	// happen for example if we're here for "sentryctl cat". When t is nil,
   518  	// degrade gracefully and retrieve what we can.
   519  	t := kernel.TaskFromContext(ctx)
   520  
   521  	buf.WriteString("  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops             \n")
   522  
   523  	for _, se := range d.kernel.ListSockets() {
   524  		s := se.Sock
   525  		if !s.TryIncRef() {
   526  			// Racing with socket destruction, this is ok.
   527  			continue
   528  		}
   529  		sops, ok := s.Impl().(socket.Socket)
   530  		if !ok {
   531  			panic(fmt.Sprintf("Found non-socket file in socket table: %+v", s))
   532  		}
   533  		if family, stype, _ := sops.Type(); family != linux.AF_INET || stype != linux.SOCK_DGRAM {
   534  			s.DecRef(ctx)
   535  			// Not udp4 socket.
   536  			continue
   537  		}
   538  
   539  		// For Linux's implementation, see net/ipv4/udp.c:udp4_format_sock().
   540  
   541  		// Field: sl; entry number.
   542  		fmt.Fprintf(buf, "%5d: ", se.ID)
   543  
   544  		// Field: local_adddress.
   545  		var localAddr linux.SockAddrInet
   546  		if t != nil {
   547  			if local, _, err := sops.GetSockName(t); err == nil {
   548  				localAddr = *local.(*linux.SockAddrInet)
   549  			}
   550  		}
   551  		writeInetAddr(buf, linux.AF_INET, &localAddr)
   552  
   553  		// Field: rem_address.
   554  		var remoteAddr linux.SockAddrInet
   555  		if t != nil {
   556  			if remote, _, err := sops.GetPeerName(t); err == nil {
   557  				remoteAddr = *remote.(*linux.SockAddrInet)
   558  			}
   559  		}
   560  		writeInetAddr(buf, linux.AF_INET, &remoteAddr)
   561  
   562  		// Field: state; socket state.
   563  		fmt.Fprintf(buf, "%02X ", sops.State())
   564  
   565  		// Field: tx_queue, rx_queue; number of packets in the transmit and
   566  		// receive queue. Unimplemented.
   567  		fmt.Fprintf(buf, "%08X:%08X ", 0, 0)
   568  
   569  		// Field: tr, tm->when. Always 0 for UDP.
   570  		fmt.Fprintf(buf, "%02X:%08X ", 0, 0)
   571  
   572  		// Field: retrnsmt. Always 0 for UDP.
   573  		fmt.Fprintf(buf, "%08X ", 0)
   574  
   575  		stat, statErr := s.Stat(ctx, vfs.StatOptions{Mask: linux.STATX_UID | linux.STATX_INO})
   576  
   577  		// Field: uid.
   578  		if statErr != nil || stat.Mask&linux.STATX_UID == 0 {
   579  			log.Warningf("Failed to retrieve uid for socket file: %v", statErr)
   580  			fmt.Fprintf(buf, "%5d ", 0)
   581  		} else {
   582  			creds := auth.CredentialsFromContext(ctx)
   583  			fmt.Fprintf(buf, "%5d ", uint32(auth.KUID(stat.UID).In(creds.UserNamespace).OrOverflow()))
   584  		}
   585  
   586  		// Field: timeout. Always 0 for UDP.
   587  		fmt.Fprintf(buf, "%8d ", 0)
   588  
   589  		// Field: inode.
   590  		if statErr != nil || stat.Mask&linux.STATX_INO == 0 {
   591  			log.Warningf("Failed to retrieve inode for socket file: %v", statErr)
   592  			fmt.Fprintf(buf, "%8d ", 0)
   593  		} else {
   594  			fmt.Fprintf(buf, "%8d ", stat.Ino)
   595  		}
   596  
   597  		// Field: ref; reference count on the socket inode. Don't count the ref
   598  		// we obtain while dereferencing the weakref to this socket.
   599  		fmt.Fprintf(buf, "%d ", s.ReadRefs()-1)
   600  
   601  		// Field: Socket struct address. Redacted due to the same reason as
   602  		// the 'Num' field in /proc/net/unix, see netUnix.ReadSeqFileData.
   603  		fmt.Fprintf(buf, "%#016p ", (*socket.Socket)(nil))
   604  
   605  		// Field: drops; number of dropped packets. Unimplemented.
   606  		fmt.Fprintf(buf, "%d", 0)
   607  
   608  		fmt.Fprintf(buf, "\n")
   609  
   610  		s.DecRef(ctx)
   611  	}
   612  	return nil
   613  }
   614  
   615  // netSnmpData implements vfs.DynamicBytesSource for /proc/net/snmp.
   616  //
   617  // +stateify savable
   618  type netSnmpData struct {
   619  	kernfs.DynamicBytesFile
   620  
   621  	stack inet.Stack
   622  }
   623  
   624  var _ dynamicInode = (*netSnmpData)(nil)
   625  
   626  // +stateify savable
   627  type snmpLine struct {
   628  	prefix string
   629  	header string
   630  }
   631  
   632  var snmp = []snmpLine{
   633  	{
   634  		prefix: "Ip",
   635  		header: "Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates",
   636  	},
   637  	{
   638  		prefix: "Icmp",
   639  		header: "InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps",
   640  	},
   641  	{
   642  		prefix: "IcmpMsg",
   643  	},
   644  	{
   645  		prefix: "Tcp",
   646  		header: "RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors",
   647  	},
   648  	{
   649  		prefix: "Udp",
   650  		header: "InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti",
   651  	},
   652  	{
   653  		prefix: "UdpLite",
   654  		header: "InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti",
   655  	},
   656  }
   657  
   658  func toSlice(a any) []uint64 {
   659  	v := reflect.Indirect(reflect.ValueOf(a))
   660  	return v.Slice(0, v.Len()).Interface().([]uint64)
   661  }
   662  
   663  func sprintSlice(s []uint64) string {
   664  	if len(s) == 0 {
   665  		return ""
   666  	}
   667  	r := fmt.Sprint(s)
   668  	return r[1 : len(r)-1] // Remove "[]" introduced by fmt of slice.
   669  }
   670  
   671  // Generate implements vfs.DynamicBytesSource.Generate.
   672  func (d *netSnmpData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   673  	types := []any{
   674  		&inet.StatSNMPIP{},
   675  		&inet.StatSNMPICMP{},
   676  		nil, // TODO(gvisor.dev/issue/628): Support IcmpMsg stats.
   677  		&inet.StatSNMPTCP{},
   678  		&inet.StatSNMPUDP{},
   679  		&inet.StatSNMPUDPLite{},
   680  	}
   681  	for i, stat := range types {
   682  		line := snmp[i]
   683  		if stat == nil {
   684  			fmt.Fprintf(buf, "%s:\n", line.prefix)
   685  			fmt.Fprintf(buf, "%s:\n", line.prefix)
   686  			continue
   687  		}
   688  		if err := d.stack.Statistics(stat, line.prefix); err != nil {
   689  			if linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
   690  				log.Infof("Failed to retrieve %s of /proc/net/snmp: %v", line.prefix, err)
   691  			} else {
   692  				log.Warningf("Failed to retrieve %s of /proc/net/snmp: %v", line.prefix, err)
   693  			}
   694  		}
   695  
   696  		fmt.Fprintf(buf, "%s: %s\n", line.prefix, line.header)
   697  
   698  		if line.prefix == "Tcp" {
   699  			tcp := stat.(*inet.StatSNMPTCP)
   700  			// "Tcp" needs special processing because MaxConn is signed. RFC 2012.
   701  			fmt.Fprintf(buf, "%s: %s %d %s\n", line.prefix, sprintSlice(tcp[:3]), int64(tcp[3]), sprintSlice(tcp[4:]))
   702  		} else {
   703  			fmt.Fprintf(buf, "%s: %s\n", line.prefix, sprintSlice(toSlice(stat)))
   704  		}
   705  	}
   706  	return nil
   707  }
   708  
   709  // netRouteData implements vfs.DynamicBytesSource for /proc/net/route.
   710  //
   711  // +stateify savable
   712  type netRouteData struct {
   713  	kernfs.DynamicBytesFile
   714  
   715  	stack inet.Stack
   716  }
   717  
   718  var _ dynamicInode = (*netRouteData)(nil)
   719  
   720  // Generate implements vfs.DynamicBytesSource.Generate.
   721  // See Linux's net/ipv4/fib_trie.c:fib_route_seq_show.
   722  func (d *netRouteData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   723  	fmt.Fprintf(buf, "%-127s\n", "Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\tMTU\tWindow\tIRTT")
   724  
   725  	interfaces := d.stack.Interfaces()
   726  	for _, rt := range d.stack.RouteTable() {
   727  		// /proc/net/route only includes ipv4 routes.
   728  		if rt.Family != linux.AF_INET {
   729  			continue
   730  		}
   731  
   732  		// /proc/net/route does not include broadcast or multicast routes.
   733  		if rt.Type == linux.RTN_BROADCAST || rt.Type == linux.RTN_MULTICAST {
   734  			continue
   735  		}
   736  
   737  		iface, ok := interfaces[rt.OutputInterface]
   738  		if !ok || iface.Name == "lo" {
   739  			continue
   740  		}
   741  
   742  		var (
   743  			gw     uint32
   744  			prefix uint32
   745  			flags  = linux.RTF_UP
   746  		)
   747  		if len(rt.GatewayAddr) == header.IPv4AddressSize {
   748  			flags |= linux.RTF_GATEWAY
   749  			gw = hostarch.ByteOrder.Uint32(rt.GatewayAddr)
   750  		}
   751  		if len(rt.DstAddr) == header.IPv4AddressSize {
   752  			prefix = hostarch.ByteOrder.Uint32(rt.DstAddr)
   753  		}
   754  		l := fmt.Sprintf(
   755  			"%s\t%08X\t%08X\t%04X\t%d\t%d\t%d\t%08X\t%d\t%d\t%d",
   756  			iface.Name,
   757  			prefix,
   758  			gw,
   759  			flags,
   760  			0, // RefCnt.
   761  			0, // Use.
   762  			0, // Metric.
   763  			(uint32(1)<<rt.DstLen)-1,
   764  			0, // MTU.
   765  			0, // Window.
   766  			0, // RTT.
   767  		)
   768  		fmt.Fprintf(buf, "%-127s\n", l)
   769  	}
   770  	return nil
   771  }
   772  
   773  // netStatData implements vfs.DynamicBytesSource for /proc/net/netstat.
   774  //
   775  // +stateify savable
   776  type netStatData struct {
   777  	kernfs.DynamicBytesFile
   778  
   779  	stack inet.Stack
   780  }
   781  
   782  var _ dynamicInode = (*netStatData)(nil)
   783  
   784  // Generate implements vfs.DynamicBytesSource.Generate.
   785  // See Linux's net/ipv4/fib_trie.c:fib_route_seq_show.
   786  func (d *netStatData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   787  	buf.WriteString("TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed " +
   788  		"EmbryonicRsts PruneCalled RcvPruned OfoPruned OutOfWindowIcmps " +
   789  		"LockDroppedIcmps ArpFilter TW TWRecycled TWKilled PAWSPassive " +
   790  		"PAWSActive PAWSEstab DelayedACKs DelayedACKLocked DelayedACKLost " +
   791  		"ListenOverflows ListenDrops TCPPrequeued TCPDirectCopyFromBacklog " +
   792  		"TCPDirectCopyFromPrequeue TCPPrequeueDropped TCPHPHits TCPHPHitsToUser " +
   793  		"TCPPureAcks TCPHPAcks TCPRenoRecovery TCPSackRecovery TCPSACKReneging " +
   794  		"TCPFACKReorder TCPSACKReorder TCPRenoReorder TCPTSReorder TCPFullUndo " +
   795  		"TCPPartialUndo TCPDSACKUndo TCPLossUndo TCPLostRetransmit " +
   796  		"TCPRenoFailures TCPSackFailures TCPLossFailures TCPFastRetrans " +
   797  		"TCPForwardRetrans TCPSlowStartRetrans TCPTimeouts TCPLossProbes " +
   798  		"TCPLossProbeRecovery TCPRenoRecoveryFail TCPSackRecoveryFail " +
   799  		"TCPSchedulerFailed TCPRcvCollapsed TCPDSACKOldSent TCPDSACKOfoSent " +
   800  		"TCPDSACKRecv TCPDSACKOfoRecv TCPAbortOnData TCPAbortOnClose " +
   801  		"TCPAbortOnMemory TCPAbortOnTimeout TCPAbortOnLinger TCPAbortFailed " +
   802  		"TCPMemoryPressures TCPSACKDiscard TCPDSACKIgnoredOld " +
   803  		"TCPDSACKIgnoredNoUndo TCPSpuriousRTOs TCPMD5NotFound TCPMD5Unexpected " +
   804  		"TCPMD5Failure TCPSackShifted TCPSackMerged TCPSackShiftFallback " +
   805  		"TCPBacklogDrop TCPMinTTLDrop TCPDeferAcceptDrop IPReversePathFilter " +
   806  		"TCPTimeWaitOverflow TCPReqQFullDoCookies TCPReqQFullDrop TCPRetransFail " +
   807  		"TCPRcvCoalesce TCPOFOQueue TCPOFODrop TCPOFOMerge TCPChallengeACK " +
   808  		"TCPSYNChallenge TCPFastOpenActive TCPFastOpenActiveFail " +
   809  		"TCPFastOpenPassive TCPFastOpenPassiveFail TCPFastOpenListenOverflow " +
   810  		"TCPFastOpenCookieReqd TCPSpuriousRtxHostQueues BusyPollRxPackets " +
   811  		"TCPAutoCorking TCPFromZeroWindowAdv TCPToZeroWindowAdv " +
   812  		"TCPWantZeroWindowAdv TCPSynRetrans TCPOrigDataSent TCPHystartTrainDetect " +
   813  		"TCPHystartTrainCwnd TCPHystartDelayDetect TCPHystartDelayCwnd " +
   814  		"TCPACKSkippedSynRecv TCPACKSkippedPAWS TCPACKSkippedSeq " +
   815  		"TCPACKSkippedFinWait2 TCPACKSkippedTimeWait TCPACKSkippedChallenge " +
   816  		"TCPWinProbe TCPKeepAlive TCPMTUPFail TCPMTUPSuccess\n")
   817  	return nil
   818  }