Read Network State Safely from /proc/net
A dashboard says an interface is dropping packets, and before you touch a config file you just want to see the kernel's own counters. /proc/net gives you interface counters, socket tables and namespace-specific network state without changing a single route, rule or service. Allow about fifteen minutes.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need a shell and a Linux system with procfs mounted; the examples normally run as an ordinary user. They were checked against Linux man-pages 6.7, from Debian package manpages 6.7-2. Contents depend on the running kernel, loaded protocols, active sockets and your network namespace, so treat sample values as shapes to recognise, not values to copy.
1. Check what /proc/net means on this host
Start by checking the path and its target. This is a read-only command and needs no elevated privileges:
$ ls -ld /proc/net /proc/self/net
lrwxrwxrwx 1 root root 8 ... /proc/net -> self/net
dr-xr-xr-x ... /proc/self/net
$ readlink /proc/net
self/net
Since Linux 2.6.25, /proc/net is a symbolic link to /proc/self/net. That is the main source of confusing results: the directory exposes the network namespace of the process reading it. A process in another namespace can see a different set of interfaces, routes and sockets even when both use the path /proc/net.
Checkpoint
Verify procfs is available and the link resolves before diagnosing an empty or unexpected table.
$ test -r /proc/self/net/dev && echo 'network procfs is readable'
network procfs is readable
2. Read interface counters without changing anything
Read /proc/net/dev to see per-interface receive and transmit counters. The first two lines describe the columns; each later line starts with an interface name and contains receive fields, then transmit fields:
$ cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 43922714770 48753419 0 0 ...
enp0s31f6: 138237730712 222401549 0 0 ...
- Bytes and packets are the useful first counters for each direction.
- Errors and drops are separate columns, not a single health score.
- A growing counter is normal on a busy link, and a zero value does not prove every packet path is healthy.
- Take two readings a few seconds apart if you need a rate.
$ awk -F: 'NR > 2 {gsub(/^ +| +$/, "", $1); print $1, $2}' /proc/net/dev
lo 43922714770 48753419 ...
enp0s31f6 138237730712 222401549 ...
Do not parse column positions by splitting only on spaces without handling the colon and repeated whitespace. Interface names and counter widths make that approach fragile.
3. Inspect TCP, UDP and UNIX socket tables
The socket files are ASCII dumps meant for inspection and debugging. Read them as snapshots: a row can disappear between two commands because the owning process closed the socket.
$ head -3 /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 ...
$ head -4 /proc/net/unix
Num RefCount Protocol Flags Type St Inode Path
0000000000000000: 00000003 00000000 00000000 0001 03 283705574
...
In the TCP and UDP tables, local_address and rem_address hold hexadecimal address and port pairs. st is the kernel socket state, while the queue fields describe kernel memory queued for the socket. The man page labels several other fields as internal and mainly useful for debugging, so avoid treating an undocumented numeric field as a stable application interface.
The UNIX table includes local sockets and can include abstract sockets: a path beginning with @ is not an ordinary filesystem pathname. The inode and path can help you connect a socket to a process, but they do not by themselves identify whether a service is trusted.
For a quick count of TCP rows, keep the header out of the count:
$ awk 'NR > 1 {n++} END {print n, "TCP rows"}' /proc/net/tcp
2 TCP rows
Your number will differ. If the file is absent or unexpectedly sparse, check the kernel and namespace context before assuming networking is broken.
4. Compare a process's network namespace
Use /proc/PID/net when you need the view belonging to a particular process. PID is a placeholder, so replace it with a real process ID. This command only reads state:
$ PID=1234
$ test -r "/proc/$PID/net/dev" && head -3 "/proc/$PID/net/dev"
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
...
Compare the interface names in /proc/self/net/dev and /proc/$PID/net/dev. Different names can be expected when the process is in a container or another network namespace, and a process can also exit while you inspect it, producing a missing path or a failed read.
Checkpoint
Record the process and namespace context with the data you collect.
$ printf 'reader pid: '; printf '%s\n' "$$"
reader pid: 12345
$ readlink /proc/self/ns/net
net:[4026531993]
$ readlink "/proc/$PID/ns/net"
net:[4026531993]
Matching namespace identifiers support a comparison; they are not a security decision. Reading another process's proc entries can also be restricted by mount options, permissions or process visibility. Use elevated privileges only when your system's access policy requires it, and do not add sudo merely because a table is interesting.
5. Check supporting files only when the question needs them
/proc/net/arpshows the kernel ARP table, including learned and preconfigured entries./proc/net/snmpholds ASCII management counters for IP, ICMP, TCP and UDP./proc/net/rawholds the raw socket table./proc/net/igmpdescribes Internet Group Management Protocol state.
These are diagnostic views, not configuration files. Some entries are conditional: the man page says /proc/net/rarp is absent when RARP is not configured into the kernel, and netfilter queue information appears at /proc/net/netfilter/nfnetlink_queue only when user-space queueing is in use. Check before reading:
$ for path in /proc/net/arp /proc/net/snmp /proc/net/rarp /proc/net/netfilter/nfnetlink_queue; do
> if test -r "$path"; then
> printf '\n%s\n' "$path"
> head -4 "$path"
> else
> printf '%s: not present or not readable\n' "$path"
> fi
> done
Do not create a missing file or redirect output back into /proc/net. These paths are kernel interfaces, and the documented operation here is reading them with tools such as cat.
Common traps
- Treating snapshots as transactions. A process can open or close a socket while you parse the output.
- Misreading hex as dotted-decimal. The address and port fields in the legacy TCP and UDP dumps are hexadecimal and encoded for kernel display.
- Forgetting the namespace. The same path can describe different data in different network namespaces.
- Reaching for a tool that is not installed. The man page describes
netstatas a cleaner interface, but many modern systems do not install it. Use an installed, documented tool such asssfor a human-facing socket view, and compare its namespace context with your proc read.
Nothing in this guide changes state, so there is no rollback step. If you redirected a proc file into a regular output file for later analysis, remove that copy once you have checked it is no longer needed; the proc entries themselves stay managed by the kernel.
Done means
- Confirmed /proc/net resolves to the reader's network namespace and is readable.
- Read /proc/net/dev with its receive and transmit columns kept distinct.
- Treated TCP, UDP or UNIX socket rows as live diagnostic snapshots.
- Compared a per-process view using
/proc/PID/netand recorded namespace context. - Tested optional files for presence and modified no proc entry.