github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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 "github.com/MerlinKodo/gvisor/pkg/abi/linux" 25 "github.com/MerlinKodo/gvisor/pkg/context" 26 "github.com/MerlinKodo/gvisor/pkg/errors/linuxerr" 27 "github.com/MerlinKodo/gvisor/pkg/hostarch" 28 "github.com/MerlinKodo/gvisor/pkg/log" 29 "github.com/MerlinKodo/gvisor/pkg/sentry/fsimpl/kernfs" 30 "github.com/MerlinKodo/gvisor/pkg/sentry/inet" 31 "github.com/MerlinKodo/gvisor/pkg/sentry/kernel" 32 "github.com/MerlinKodo/gvisor/pkg/sentry/kernel/auth" 33 "github.com/MerlinKodo/gvisor/pkg/sentry/socket" 34 "github.com/MerlinKodo/gvisor/pkg/sentry/socket/unix" 35 "github.com/MerlinKodo/gvisor/pkg/sentry/socket/unix/transport" 36 "github.com/MerlinKodo/gvisor/pkg/sentry/vfs" 37 "github.com/MerlinKodo/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 deferencing 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 deferencing 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 }