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 man7.org .)
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
sschoking. - When the
ssmanpage 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 withss. - 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 '192.168.1.0/24'.
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 127.0.0.0/8 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.