Some usage notes for the Linux ss program

December 20, 2021

Today I tweeted:

The Linux ss command is one of those things that I should learn one of these days but I keep just using lsof unless lsof is going to be really inconvenient (or slow).

In the way of these things, this prompted me to go poke at ss again. It turns out that something important has happened in the world of ss since I last looked at it in late 2018, namely ss's very useful filtering language is now actually documented in its manual page.

(This development is from early 2021 and may not be in your Linux distribution's manpage collection yet. But it's in the online manpage at

There are still some things that aren't obvious in the manpage, especially about filters. In no particular order:

  • The filter expression can be split up over multiple command line arguments, but doesn't have to be. My old example used a single command line argument for the filter expression but I also have other ones that use a mixture, including:
    [...] 'dport = :8025' dst $SPECIALIP

    This splits the filter expression across three command line arguments without ss choking.

  • When the ss manpage talks about 'source' and 'destination' hosts or ports, it really means 'local' and 'remote' respectively. Because I normally deal with firewalls, 'source' and 'destination' generally have packet or connection specific meanings to me, and I have to reset my assumptions when dealing with ss.

  • State filters can be written in a sequence, eg 'state established state listening' will list established and listening sockets.

  • As documented at the end of the manual page's HOST SYNTAX, the 'host' for IP addresses can also be a CIDR network address such as ''.

My traditional use of lsof in this context is 'lsof -n -i', perhaps with some filtering to look at only listening sockets. Unfortunately there's no exact analog of this command, so the arguments you want to use depend on what you're trying to do. First, by default ss doesn't show listening sockets; if you give -l you get only listening sockets, and if you really want all sockets, both listening and non-listening, you need to use -a.

Although it is not documented, you can combine -t and -u to get both TCP and UDP sockets. However, you cannot combine -4 and -6 to get both IPv4 and IPv6 sockets; you must pick one. To get just IPv4 and IPv6 together, I think that '-A inet' works, but I'm not sure I understand socket tables correctly here. You can also use '-tu', which is usually close enough to what you want.

Lsof will show you the PID, the program name of the PID, and the login name of the owner of the process. There is no convenient direct equivalent of this in ss; you can show the process with -p, and sometimes show the UID (along with varying other things) with -e, but there's no option to turn the UID into a login name even when it's shown. In addition, lsof and ss give you somewhat different information, technically. Lsof is working forward from what file descriptors a process has open and what UID those processes are running as; as it says, ss is working backward from who the kernel considers to own a socket. It's possible for ss to list multiple processes for a given socket. Based on my experimentation, ss is not a useful and reliable way of easily showing who 'owns' a particular socket, especially a listening one; if you need this, use lsof.

(According to ss's manpage, the -e option shows 'the user id the socket belongs to', implicitly as the kernel sees it. Evidently the kernel sometimes doesn't consider the socket to belong to anyone. People who understand Linux socket internals can probably tell you all about when and why this happens, but I know is that -e has highly variable and not entirely useful output for my purposes. The -p option appears to be reliable.)

I think this means that 'ss -tupa' the closes equivalent to 'lsof -n -i', but usually I actually want only listening sockets, which makes that 'ss -tupl'. If I want to know what the active connections are and don't care about listening things, I want 'ss -tup' instead.

(Credit goes to @bitprophet, who gave me starting arguments. I skip the '-d' argument because I rather doubt we ever have any DCCP sockets on our systems.)

For sysadmin purposes, where I often care 'what is listening to the world instead of just localhost', what I want is:

ss -tulp '! ( src or src [::1] )'

Some of the time, I will want '-n' as well so that ss stops decoding high port numbers to whatever is listed in /etc/services as once upon a time being vaguely associated with that port. For some purposes, you can add 'sport gt 1024' to ignore ports that require root's involvement to listen on.

(This is where I wish all tools had an option that used names only for ports under 1024, which really are solidly assigned, and listed all others numerically.)

PS: ss may not show a process associated with (listening) ports under some circumstances, such as if you have the kernel Wireguard implementation listening on some port. As I do. You can also see this for some ports on a NFS server, for the same reason.

Comments on this page:

By Nieve at 2021-12-21 18:29:00:

Using ss from iproute2-5.13.0 on open SUSE I get user names for ss -tupl output, like so:

tcp LISTEN 0 511* users:(("Discord",pid=24189,fd=92))

Sadly that probably doesn't mean the owner is any more reliable.

By Nieve at 2021-12-21 21:26:04:

Ignore that, I had the wrong meaning of users.

Written on 20 December 2021.
« My Firefox bookmarklet to see links I've visited more reliably
Our internal network access authentication needs »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Dec 20 23:19:50 2021
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.