Two years ago, I described a simple way to display established TCP connections using ss command. Today I will use lsof and gawk to pretty print network connections.

Notice, I am using gawk to take advantage of the gensub function.

Display listening TCP ports

Use the following command to display listening TCP ports.

$ sudo lsof -iTCP -sTCP:LISTEN -P -n | \
       sed 1d | \
       gawk '{ 
         if (substr($9,1,1) != "[") {
           split($9,local,":");
         } else {
           local[1]=gensub(/\[(.*)\]:.*/,"\\1","g",$9);
           local[2]=gensub(/\[.*\]:(.*)/,"\\1","g",$9);
         };
         cmd="cat /proc/" $2 "/cmdline | tr '\''\\000'\'' '\'' '\''"; 
         cmd | getline output; 
         close(cmd);
         printf "Process %5s is listening on %s %s port %s -- %s \n",$2,$5,local[1],local[2],output
       }' | \
       sort

Sample output.

Process 13278 is listening on IPv4 127.0.0.1 port 631 -- /usr/sbin/cupsd -l
Process 13278 is listening on IPv6 ::1 port 631 -- /usr/sbin/cupsd -l
Process  1138 is listening on IPv4 * port 22 -- /usr/sbin/sshd -D
Process  1138 is listening on IPv6 * port 22 -- /usr/sbin/sshd -D
Process  1810 is listening on IPv4 127.0.1.1 port 53 -- /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
Process  8418 is listening on IPv4 127.0.0.1 port 4000 -- /usr/bin/ruby /usr/bin/jekyll serve --future -w

Display UDP connections

Use the following command to display UDP connections. I have extended it a bit due to the stateless nature of the UDP protocol.

$ sudo lsof -iUDP -P -n | \
       sed 1d | \
       gawk '{
         split($9,address,"->");
         if (substr(address[1],1,1) != "[") {
           split(address[1],local,":");
         } else {
           local[1]=gensub(/\[(.*)\]:.*/,"\\1","g",address[1]);
           local[2]=gensub(/\[.*\]:(.*)/,"\\1","g",address[1]);
         };
         if (substr(address[2],1,1) != "[") {
           split(address[2],remote,":");
         } else {
           remote[1]=gensub(/\[(.*)\]:.*/,"\\1","g",address[2]);
           remote[2]=gensub(/\[.*\]:(.*)/,"\\1","g",address[2]);
         };
         local_address="local address " $5 " " local[1] " port " local[2];
         if (length(remote[1])>0) {
           remote_address="remote address " remote[1] " port " remote[2];
         } else {
           remote_address="";
         }
         cmd="cat /proc/" $2 "/cmdline | tr '\''\\000'\'' '\'' '\''"; 
         cmd | getline output; 
         close(cmd);
         printf "Process %5s %s %s -- %s \n",$2,local_address,remote_address, output;
       }' | \
       sort

Sample output.

Process 17135 local address IPv4 * port 631  -- /usr/sbin/cups-browsed
Process 25997 local address IPv4 * port 68  -- /sbin/dhclient -d -q -sf /usr/lib/NetworkManager/nm-dhcp-helper -pf /var/run/dhclient-wlp2s0.pid -lf /var/lib/NetworkManager/dhclient-c6755d45-8401-405e-ac0f-029516cbd09f-wlp2s0.lease -cf /var/lib/NetworkManager/dhclient-wlp2s0.conf wlp2s0
Process  1810 local address IPv4 127.0.1.1 port 53  -- /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
Process  1810 local address IPv4 * port 55324  -- /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
Process  2161 local address IPv4 * port 5353  -- /opt/google/chrome/chrome
Process  2161 local address IPv6 * port 5353  -- /opt/google/chrome/chrome
Process   908 local address IPv4 * port 40303  -- avahi-daemon: running [notebook.local]
Process   908 local address IPv4 * port 5353  -- avahi-daemon: running [notebook.local]
Process   908 local address IPv6 * port 5353  -- avahi-daemon: running [notebook.local]
Process   908 local address IPv6 * port 54270  -- avahi-daemon: running [notebook.local]

Display established TCP connections

Use the following command to display established TCP connections.

$ sudo lsof -iTCP -sTCP:ESTABLISHED -P -n | \
       sed 1d | \
       gawk '{
         split($9,address,"->");
         if (substr(address[1],1,1) != "[") {
           split(address[1],local,":");
         } else {
           local[1]=gensub(/\[(.*)\]:.*/,"\\1","g",address[1]);
           local[2]=gensub(/\[.*\]:(.*)/,"\\1","g",address[1]);
         };
         if (substr(address[2],1,1) != "[") {
           split(address[2],remote,":");
         } else {
           remote[1]=gensub(/\[(.*)\]:.*/,"\\1","g",address[2]);
           remote[2]=gensub(/\[.*\]:(.*)/,"\\1","g",address[2]);
         };
         local_address="local address " $5 " "  local[1] " port " local[2];
         if (length(remote[1])>0) {
           remote_address="remote address " remote[1] " port " remote[2];
         } else {
           remote_address="";
         }
         cmd="cat /proc/" $2 "/cmdline | tr '\''\\000'\'' '\'' '\''"; 
         cmd | getline output; 
         close(cmd);
         printf "Process %5s %s %s -- %s \n",$2,local_address,remote_address, output;
       }' | \
       sort

Sample output.

Process 30572 local address IPv4 192.168.1.177 port 33286 remote address 93.184.216.34 port 443 -- nextcloud
Process  2161 local address IPv4 192.168.1.177 port 33428 remote address 93.184.216.34 port 443 -- /opt/google/chrome/chrome
Process  2161 local address IPv4 192.168.1.177 port 43736 remote address 93.184.216.34 port 5228 -- /opt/google/chrome/chrome

Display all network connections

Use the following command to display every connection with additional information.

$ sudo lsof -i  -P -n | \
       sed 1d | \
       gawk '{
         split($9,address,"->");
         if (substr(address[1],1,1) != "[") {
           split(address[1],local,":");}
         else {
           local[1]=gensub(/\[(.*)\]:.*/,"\\1","g",address[1]);
           local[2]=gensub(/\[.*\]:(.*)/,"\\1","g",address[1]);
         };
         if (substr(address[2],1,1) != "[") {
           split(address[2],remote,":");}
         else {
           remote[1]=gensub(/\[(.*)\]:.*/,"\\1","g",address[2]);
           remote[2]=gensub(/\[.*\]:(.*)/,"\\1","g",address[2]);
         };
         local_address="local address " local[1] " port " local[2];
         if(length(remote[1])>0) {
           remote_address="remote address " remote[1] " port " remote[2];
         } else {
           remote_address="";
         };
         gsub(/[()]/,"",$10);
         cmd="cat /proc/" $2 "/cmdline | tr '\''\\000'\'' '\'' '\''";
         cmd | getline output;
         close(cmd);
         printf "Process %5s %s %s %s %s %s -- %s \n",$2,$5,$8,local_address,remote_address,$10,output;
       }' | \
       sort

Sample output.

Process 17135 IPv4 UDP local address * port 631   -- /usr/sbin/cups-browsed
Process 25997 IPv4 UDP local address * port 68   -- /sbin/dhclient -d -q -sf /usr/lib/NetworkManager/nm-dhcp-helper -pf /var/run/dhclient-wlp2s0.pid -lf /var/lib/NetworkManager/dhclient-c6755d45-8401-405e-ac0f-029516cbd09f-wlp2s0.lease -cf /var/lib/NetworkManager/dhclient-wlp2s0.conf wlp2s0
Process 30572 IPv4 TCP local address 192.168.1.177 port 33286 remote address 93.184.216.34 port 443 ESTABLISHED -- nextcloud
Process  1138 IPv4 TCP local address * port 22  LISTEN -- /usr/sbin/sshd -D
Process  1138 IPv6 TCP local address * port 22  LISTEN -- /usr/sbin/sshd -D
Process  1810 IPv4 TCP local address 127.0.1.1 port 53  LISTEN -- /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
Process  1810 IPv4 UDP local address 127.0.1.1 port 53   -- /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
Process  1810 IPv4 UDP local address * port 55324   -- /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
Process  2161 IPv4 TCP local address 127.0.0.1 port 34708 remote address 127.0.0.1 port 4000 CLOSE_WAIT -- /opt/google/chrome/chrome
Process  2161 IPv4 TCP local address 192.168.1.177 port 33428 remote address 93.184.216.34 port 443 ESTABLISHED -- /opt/google/chrome/chrome
Process  2161 IPv4 TCP local address 192.168.1.177 port 43736 remote address 93.184.216.34 port 5228 ESTABLISHED -- /opt/google/chrome/chrome
Process  2161 IPv4 UDP local address * port 5353   -- /opt/google/chrome/chrome
Process  2161 IPv6 UDP local address * port 5353   -- /opt/google/chrome/chrome
Process  2962 IPv4 TCP local address 192.168.1.177 port 33600 remote address 93.184.216.34 port 443 ESTABLISHED -- /usr/bin/QOwnNotes
Process  8418 IPv4 TCP local address 127.0.0.1 port 4000  LISTEN -- /usr/bin/ruby /usr/bin/jekyll serve --future -w
Process   908 IPv4 UDP local address * port 40303   -- avahi-daemon: running [notebook.local]
Process   908 IPv4 UDP local address * port 5353   -- avahi-daemon: running [notebook.local]
Process   908 IPv6 UDP local address * port 5353   -- avahi-daemon: running [notebook.local]
Process   908 IPv6 UDP local address * port 54270   -- avahi-daemon: running [notebook.local]