Home / Alt manpages / proc_pid_net(5)

  • proc_pid_net(5)
  • File format
  • linux

Read Linux Network State Safely through /proc/pid/net

You will finish with a small set of read-only commands for inspecting interfaces, sockets, ARP entries and protocol counters in the network namespace of a Linux process. The examples use the locally installed proc_pid_net(5) from the manpages package version 6.7-2, on a Linux 6.8.0-139-generic kernel.

Allow about ten minutes. You need a shell and a process ID that you can inspect. These commands read kernel-exported files only: they do not bring interfaces up or down, change routes, close sockets, flush ARP entries or alter firewall rules. Elevated privileges are not normally needed for the examples, although permissions and namespace access can vary between systems.

1. Establish which network namespace you are reading

Start with your own shell process. The special path /proc/self/net follows the network namespace of the process opening it, while /proc/net is a symbolic link to that same path:

$ printf 'shell PID: %s\n' "$$"
shell PID: 4058756
$ readlink /proc/net
self/net
$ readlink /proc/$$/net
net

Your PID and the exact readlink output may differ. The useful check is the relationship: /proc/net resolves through /proc/self/net, so a command reading it sees the namespace of that command's process.

For another process, replace PID with a real numeric process ID:

$ PID=12345
$ readlink "/proc/$PID/ns/net"
net:[4026531993]
$ sed -n '1,5p' "/proc/$PID/net/dev"

The namespace inode is an identifier, not a network configuration dump. Comparing it with another process tells you whether both processes are attached to the same network namespace. The net directory under /proc/PID has exposed network information since Linux 2.6.25.

2. Check interface counters in dev

Read /proc/net/dev for the current namespace:

$ sed -n '1,8p' /proc/net/dev
Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    lo: 44116814658 48870829    0    0    0     0          0         0 44116814658 48870829    0    0    0     0       0          0
enp0s31f6: 141490041774 225089972    0    0    0     4          0         4 765588029594 566055946    0    0    0     0       0          0

The file is an ASCII view of device status. Each interface has receive and transmit columns for bytes, packets and several error or drop counters. The header is part of the format: keep it when saving output so that a later reader can map the numbers correctly.

Checkpoint: confirm that an interface you expect to exist is present:

$ awk -F: 'NR > 2 {gsub(/^ +| +$/, "", $1); print $1}' /proc/net/dev
lo
enp0s31f6
docker0

Interface names are namespace-specific. An interface missing here may exist in another namespace, rather than being absent from the host altogether.

3. Inspect sockets without treating the output as an API

The tcp and udp files expose kernel socket tables. Read their headings and a few rows:

$ sed -n '1,5p' /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
   0: 0100007F:734A 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 66575267 1 0000000000000000 100 0 0 10 0
$ sed -n '1,5p' /proc/net/udp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops
 311: 0100007F:D4C1 3500007F:0035 01 00000000:00000000 00:00000000 00000000   130        0 187570908 2 0000000000000000 0

Addresses and ports in these tables are encoded for the kernel's internal display format, and the state field is an internal value. The manpage describes these files as useful mainly for debugging. Do not build a long-lived parser around column positions or assume that a row is stable while you inspect it. For ordinary socket inspection, a tool such as ss provides a cleaner interface.

The uid field identifies the effective user ID of the socket creator. It is not a service name, process ID or proof that the socket is still owned by the same application. Socket tables can change between reads.

4. Use unix for local sockets

/proc/net/unix lists UNIX domain sockets in the current namespace and shows their status:

$ sed -n '1,8p' /proc/net/unix
Num       RefCount Protocol Flags    Type St Inode Path
0000000000000000: 00000003 00000000 00000000 0001 03 283705574
0000000000000000: 00000003 00000000 00000000 0001 03 264202087 /run/containerd/s/example

Some rows have no path because the socket is not bound to a pathname. Abstract-namespace sockets are shown with a path beginning with @. Do not interpret a blank path as a failed read.

When a path is present, treat it as diagnostic data. It can reveal service names, runtime directories and other local topology. Avoid pasting unrestricted socket listings into public bug reports if host details are sensitive.

5. Read ARP and protocol counters

For IPv4 address resolution, inspect arp:

$ sed -n '1,6p' /proc/net/arp
IP address       HW type     Flags     HW address            Mask     Device
172.17.0.9       0x1         0x2       ea:00:02:f9:c1:c2     *        docker0

The table includes dynamically learned and pre-programmed ARP entries. The hardware address is the link-layer mapping when known; the device column tells you which interface the entry belongs to. An empty table can be normal on a host that has not learned any entries in this namespace.

/proc/net/snmp contains ASCII counters for the IP, ICMP, TCP and UDP management information bases:

$ sed -n '1,4p' /proc/net/snmp
Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes
Ip: 1 64 212918847 0 0 6091297 0 0 205956291 266165014 40 0
Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds
Icmp: 274344 26 0 1091 54

Each protocol usually contributes a header row followed by a values row. Read both rows together. These are cumulative counters, so a single snapshot cannot tell you a rate. Take two snapshots over a known interval and compare the same named field, accounting for counter wrap or reset.

6. Compare a process with the host view

To check whether a process sees the same network namespace as your shell, compare the namespace links and one harmless file:

$ PID=12345
$ printf 'shell:  '; readlink /proc/self/ns/net
shell:  net:[4026531993]
$ printf 'target: '; readlink "/proc/$PID/ns/net"
target: net:[4026531993]
$ diff -u /proc/net/dev "/proc/$PID/net/dev"

No output from diff means the two snapshots matched. A difference can be caused by a different namespace or by counters changing between the reads, so check the namespace identifiers first. The target process may also exit while you are reading it, which produces an ordinary /proc error rather than a network diagnosis.

Do not add sudo automatically. First try the read as the account that will run the diagnostic. If /proc/PID/net is denied, record that fact and use the least privilege permitted by your system's process and procfs policy. Reading a different process's network data can expose socket identities and topology, so privilege boundaries are part of the result.

Nothing in this guide changes system state, so there is no rollback step. Stop reading if the output contains addresses, socket paths or other details that your incident-handling policy says must remain private.

Done means

  • You identified the network namespace behind the path you read.
  • You checked interface counters in /proc/net/dev and retained the header.
  • You treated TCP and UDP tables as debugging output, not a stable scripting API.
  • You know that ARP, UNIX socket and SNMP data are namespace-specific and may change while being read.
  • You compared /proc/self/ns/net with a target process before explaining a difference.