Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Read /proc/net/tcp If ss, nc, netstat, telnet and curl Aren't Installed

read-proc-net-tcp-if-ss-nc-netstat-telnet-and-curl-are-not-installed.jpg

If you want to see if a process is bound to a port there's a few tools you can use but if they are not there you can cat /proc/net/tcp.

Quick Jump:

Prefer video? Here it is on YouTube.

I found myself in a situation where ss, nc, netstat, telnet and curl weren’t available and I wasn’t able to install them. In case you didn’t know, curl can create telnet connections.

All of these tools let you see if an address is bound to a specific port. Maybe you’re troubleshooting something and you want to make sure that the process you care about is really running on a specific port and bound to a specific address like 0.0.0.0 or 127.0.0.1 depending on what you’re doing.

If you’re running things in a containerized world using Docker or Kubernetes, you could be in a super locked down container running as a non-root user with no user escalation permissions and very few utilities installed.

In the Kubernetes case you can always spawn a debug container to analyze the state of another container with custom tools but you might not be using Kubernetes or maybe the environment you’re in won’t allow you to spin up new containers.

This is where learning how to read cat /proc/net/tcp or cat /proc/net/tcp6 could be useful.

# What is /proc/net/tcp[6]?

  • On Linux systems a process will write its address:port info in its protocol file
    • If you’re on macOS /proc/net won’t be available but if you happen to be running a Linux container, you can connect to it and find details about that container’s ports
  • There’s tcp, udp and many other network related files in /proc/net
    • You’ll often see tcp and tcp6 depending on if a socket is bound to IPv4 or IPv6

Containerized Processes with Docker

If you’re using Docker and you have a container running with something like -p 8080:80 and you want to check the port on your host then you’ll want to use cat /proc/net/tcp6 because as far as I know Docker will bind a port back to your host over an IPv6 wildcard socket (::) and that socket is able to handle both IPv4 and IPv6 traffic for all addresses.

If instead you used -p 127.0.0.1:8080:80 then Docker will use an IPv4 socket and you’ll find it in cat /proc/net/tcp. That’s an important detail if you’re wondering why it might be different depending on which projects you’re working on.

# Parsing the Output of /proc/net/tcp[6]

The way to parse both files (tcp6 vs tcp) is the same.

There’s a number of columns but we’re only going to focus on local_address. I’ve removed the other columns so it’s easier to read here.

Here’s the state of the system on my dev box. In a bit we’ll go over an example Docker command so you can follow along:

tcp6 vs tcp

$ cat /proc/net/tcp6
  sl  local_address
   0: 00000000000000000000000000000000:0521
  • 0 (first) entry is Hugo running and published on 0.0.0.0:1313 through Docker
$ cat /proc/net/tcp
  sl  local_address
   0: 0100007F:1F90
  • 0 (first) entry is NGiNX running and published on 127.0.0.1:8080 through Docker

16-Bit Hexadecimal Port

The local_address ends with :XXXX which is the port in 16-bit hexadecimal format.

On the command line you can use printf to convert this to decimal along with many other tools. With most tools, make sure your hex number starts with 0x:

$ printf "%d\n" 0x0521
1313
$ printf "%d\n" 0x1F90
8080

16-Bit Hexadecimal Address

The IPv6 and IPv4 address values for :: and 0.0.0.0 are:

  • 00000000000000000000000000000000
  • 00000000

For these you probably won’t need to convert them to decimal but there’s quite a few details to be aware of when you do need to do a conversion.

For example 0100007F is the IPv4 address for 127.0.0.1.

Here’s a workflow to convert that into an IP address:

  • Break it up into bytes which are chunks of 2 characters such as 01 00 00 7F
  • Reverse it to 7F 00 00 01
    • It’s in little-endian format (stores the least-significant byte at the smallest address)
  • Convert each byte into decimal:
    • 7F is 127
    • 00 is 0
    • 00 is 0
    • 01 is 1

Boom, we just arrived at 127.0.0.1. Funny enough after seeing this enough you’ll be able to pick out 0100007F as 127.0.0.1 without going through that process every time.

Converting Decimal into 16-bit Hexadecimal

If you have a bunch of entries in /proc/net/tcp and you want to look for a specific port you can always run grep ":XXXX" /proc/net/tcp and replace XXXX with the hex value you want.

This is where knowing how to do the conversion helps because you might want to convert 5432 (decimal) into the value you’ll be grepping for (hex):

$ printf "%04X\n" 5432
1538

You can verify this with the ports we used above (1313 / 8080):

$ printf "%04X\n" 1313
0521

$ printf "%04X\n" 8080
1F90

Pretty handy! Now you have enough information to parse this output and search for specific ports to see if your process is listening and running correctly.

With that said, if you have tools like ss or netstat available, it’s much easier to get this information since it converts and aligns everything for you:

$ ss -ltpn
State     Recv-Q    Send-Q    Local Address:Port    Peer Address:Port    Process
LISTEN    0         4096          127.0.0.1:8080            0.0.0.0:*
LISTEN    0         4096                  *:1313                  *:*

# Following Along With Docker

This will further drive home the above examples by showing different values depending on if we’re talking about the host’s machine or inside of the container and if you’re on macOS it will also let you follow along.

To produce the NGiNX output I used before, I ran this command:
docker container run --rm --name procnet -p 127.0.0.1:8080:80 nginx:mainline-bookworm

Now you can run this in a 2nd terminal:

$ docker container exec -it procnet cat /proc/net/tcp
  sl  local_address
   0: 00000000:0050

It’s interesting right?

On the host, we published this information:

  • The address was bound to 127.0.0.1 but here we see 0.0.0.0 for the address
  • The port was bound to 8080 but here we see 0050 which converts to 80

All of the above makes sense. We’re now running cat /proc/net/tcp inside of the container so the host and port information is related to that.

The NGiNX image we use binds to 0.0.0.0 and runs on port 80. As a separate step we’ve published that to 127.0.0.1:8080 on the host.

Technically you can run docker container exec procnet cat /proc/net/tcp6 too and see the IPv6 information because inside of the container NGiNX binds on both IPv4 and IPv6.

I thought it would be helpful to include the above so you can see both sides of how an address:port works in a container having its address:port published back to the host.

The video below goes over all of the above.

# Demo Video

Timestamps

  • 0:33 – Use cases
  • 1:12 – The basics of /proc/net/tcp
  • 2:58 – How Docker binds addresses
  • 4:57 – What we’re going to cover and hexadecimal formats
  • 6:48 – Parsing the port value
  • 7:38 – Parsing the address value
  • 9:29 – Converting decimal to hexadecimal for filtering
  • 10:53 – Use ss, netstat, etc. if you have it
  • 11:27 – Checking out /proc/net/tcp in a container

When was the last time you had to manually parse /proc/net? Let me know below.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per year (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments