93

I'd like to monitor responses on a unix socket without disturbing the original connections and pipe them to a script for processing.

I know how to do this with tcpdump for tcp connections but I cannot seem to find a solution for local unix sockets.

Is this even possible?

1

6 Answers 6

19

There's a guy that claims to do so by creating an app that acts as a gateway between two sockets and logging all data that flows. So you can't tap on a socket but if you can restart the service and tune it to use this guy app you will be able to see all traffic.

Here is the link to the post: Unix Socket Sniffer

There's another way that needs you to find the process id attached to the socket, then find with lsof the file descriptor of the socket and then tap the file descriptor using strace.

If you can stop whatever client/server is using the socket and reconfigure it I would recommend always the first method, second method it's tricky and requires you to tap a current process which on some apps could cause it to crash.

Hope someone enlighten us with anoter way :)

Good luck

3
  • Yah, you can do the middleman method also with socat but I am hoping for a more direct way without modifying existing settings elsewhere.
    – ck_
    Commented Oct 8, 2012 at 1:15
  • 1
    Then lsof and strace it's the only way I'm aware of. Watch out on produciton when you detach strace from the process, check that everything keeps running after that.
    – Valor
    Commented Oct 8, 2012 at 1:31
  • 3
    After some more digging around I found a similar question with some details about why this is not directly possible over on stackoverflow stackoverflow.com/questions/8394613/…
    – ck_
    Commented Oct 8, 2012 at 13:56
119

you can use socat.

sudo mv /path/to/sock /path/to/sock.original
sudo socat -t100 -x -v UNIX-LISTEN:/path/to/sock,mode=777,reuseaddr,fork UNIX-CONNECT:/path/to/sock.original

What is happening above: First move original socket to sock.original. Socat creates a new socket ('UNIX-LISTEN') in the originals location and forwards all to the original ('UNIX-connect'). The -v tells socat to also print output to STDERR.

2
  • 3
    Care to add a little more explanation?
    – Kazark
    Commented Apr 1, 2013 at 16:10
  • 6
    That's easy when the original unix socket has a path on the filesystem. but what if it's an abstract namespace unix socket that you cannot actually move? Commented Jun 14, 2016 at 17:59
13

You might also try using strace on one of the processes on either side of the socket, since this will let you watch what is written/read. I found in my production environments, I don't have socat, but do have strace.

For any useful purpose, setting -s to something big is a must.

4
  • This worked well for me, and easy to do. Use strace -p <pid> to watch a running process. Commented Feb 25, 2016 at 3:07
  • quick command: strace -s9999 -f $(for i in $( pidof php5-fpm ) ; do echo -n " -p $i "; done ) 2>&1 | tee /tmp/php.log and then run the tests. You have the /tmp/php.log to slowly check if the log is too big. If you are getting too much traffic, do a request with a query-string with your name or something so you can search for it in the logs
    – higuita
    Commented Apr 18, 2017 at 16:51
  • 1
    @higuita I know it's been a long time, but instead of that loop you can let printf handle the repetition. printf " -p %s" $(pidof php5-fpm) will prefix each pid argument with -p and is much more practical to write.
    – JoL
    Commented Jul 20, 2018 at 1:26
  • 1
    strace -ff -tt -yy -x -e read=all -e write=all curl https://www.google.com/ strace can even label the UDS and dump all data read and written in hex
    – tompreston
    Commented Jul 22, 2021 at 13:40
13
// backup the socket
sudo mv /var/run/docker.sock /var/run/docker.sock.original

// use tcp port 8089 proxy the original socket
sudo socat TCP-LISTEN:8089,reuseaddr,fork UNIX-CONNECT:/var/run/docker.sock.original

// use the new socket to proxy the 8089 port
sudo socat UNIX-LISTEN:/var/run/docker.sock,fork TCP-CONNECT:127.0.0.1:8089

then:

sudo tcpdump -i lo -netvv port 8089
2
  • Great idea, but sadly, I couldn't get this to work for me. In this case, I am trying to find the comms between rsyslogd and journald. I adopted the socket names, everything was in place as it should be, but (1) I had to run the socats in the background (this would have been nice to know), and yet, tcpdump showed nothing. :-(
    – Otheus
    Commented Mar 31, 2021 at 14:13
  • lsof -U shows... rsyslogd 29660 root 3u unix ... run/systemd/journal/syslog.orig socat 29708 root 5u unix ... /var/run/systemd/journal/syslog and netstat ... tcp 0 0 0.0.0.0:8089 0.0.0.0:* LISTEN 29707/socat lo0 interface whitelisted in iptables both directions.
    – Otheus
    Commented Mar 31, 2021 at 14:15
2

A bit late, but I modified a systemtap script that will do this for people who can't forward socket traffic from a listening process. Use at your own peril, I have only tested this for Red Hat Enterprise Linux 7 but the structures we reference are generic and (hopefully) don't change very much:

/*
 * watch_unix_socket.stp
 *
 * This is a simply more modern version of the script found here:
 * https://sourceware.org/systemtap/wiki/WSunixSockets
 *
 * The first argument is the location of the file descriptor for a UNIX socket.
 * To find this address, for example, for the Docker socket run:
 *
 * # lsof 2>&1 | awk '/docker.sock/ {print $7}' | grep -v '0t0' | sort -u
 * 0xffff8ed0b4eb1800
 *
 * And use that address to run this systemtap script:
 * 
 * # stap watch_unix_socket.stp 0xffff8ed0b4eb1800
 */

probe begin {
    printf("Watching input into socket 0x%x...\n", $1);
}

probe kernel.function("unix_stream_sendmsg") {
    if ($sock->sk != $1) {
        printf("%d %s is accessing %p\n", pid(), execname(), $sock->sk);
        printf("====================\n");

        len = 0
        for (i = 0; i < $msg->msg_iovlen; i++) {
            len += $msg->msg_iov[i]->iov_len;
        }
    
        printf("%d [", len);
        for (i = 0; i < $msg->msg_iovlen; i++) {
            printf("%s", user_string_n($msg->msg_iov[i]->iov_base, $msg->msg_iov[i]->iov_len));
        }

        printf("] [");
        for (i = 0; i < $msg->msg_iovlen; i++) {
            printf("%s", user_string_n($msg->msg_iov[i]->iov_base, $msg->msg_iov[i]->iov_len));
        }

        printf("]\n\n");
    }
}

I keep it updated as a Github Gist on my page.

0

If someone is still looking for an answer in 2024, try this sockdump tool. The tool is based on bpf, and the author also has an answer on unix stackexchange. It worked for me on 6.1.0-18-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux. A sample run -

$ sudo ./sockdump.py --format string /tmp/unix_socket.event.fs
waiting for data
19:19:30.941 >>> process switch [76076 -> 76104] path /tmp/unix_socket.event.fs len 28(28)
Content-Type: auth/request

19:19:30.941 >>> process terver [76104 -> 76076] path /tmp/unix_socket.event.fs len 16(16)
auth abcdefgh

19:20:05.620 >>> process switch [76076 -> 76104] path /tmp/unix_socket.event.fs len 57(57)
Content-Type: text/disconnect-notice
Content-Length: 67

19:20:05.620 >>> process switch [76076 -> 76104] path /tmp/unix_socket.event.fs len 67(67)
Disconnected, goodbye.

PS: this requires installing BCC tools from iovisor, and I believe kernel version >= 5.10.

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Mar 5 at 16:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .