What I think you want to do is get a list of listening ports and then remove them from any other TCP connections, then that will be all of the outgoing connections. The ss (socket status) command outputs "Local Address:Port" and "Peer Address:Port" columns, we need to remove the listening ports from the "Local Address:Port" column and not the "Peer Address:Port" column, otherwise you may miss some outgoing connections. So to achieve that I'm using \s{2}+
behind the ":$port" string in the grep to match on the spaces that exist behind the "Local Address:Port" column; that column has two or more white spaces behind it, where the "Peer Address:Port" has one space and then a newline (grrr... should just have a newline, IMO, then I could have used \s+
instead of \s{2}+
.) Normally I might try to use the filtering functionality of ss, like with ss -tn state established '(sport != :<port1> and sport !=:<port2>)' src <ip address>
. But it appears there is a limit on how long that string can be, it bombed out on a system where I had a lot of listening ports. So I'm trying to do the same thing with grep. I believe the following will work:
FILTER=$(ss -tn state listening | gawk 'NR > 1 {n=split($3,A,":"); B[NR-1]=A[n]} END {for (i=1; i<length(B); i++) printf ":%s\\s{2}+|", B[i]; printf ":%s\\s{2}+", B[i]}')
ss -tn state established dst :* | grep -P -v "$FILTER"
Note this depends on the version of ss you're using, older versions (like: ss utility, iproute2-ss111117) has a different output format, so you may have to use $3 instead of $4 in awk. Note also ss -tln
and ss -tn state listening
gives you different output, which is a little counter-intuitive to me. YMMV.
I found a slightly more elegant solution that doesn't require knowing host's IP, ss -tn state established dst :*
works well, I modified the command lines above.