1

I tried to dump all data sent by a specific process on Linux by hooking a handler to the Kernel's function sock_sendmsg() defined in linux/socket.c. I could do that by writing a systemtap probe handler for probe kernel.function("sock_sendmsg@net/socket.c") that dumps all data blocks passed with the 2nd argument struct msghdr *msg.

Here's the excerpt from net/socket.c:

int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
    struct kiocb iocb;
    struct sock_iocb siocb;
    int ret;

    init_sync_kiocb(&iocb, NULL);
    iocb.private = &siocb;
    ret = __sock_sendmsg(&iocb, sock, msg, size);
    if (-EIOCBQUEUED == ret)
            ret = wait_on_sync_kiocb(&iocb);
    return ret;
}

I tested my systemtap script hook_sendmsg.stp. First I ran hook_sendmsg.stp in one terminal. Then I opened another terminal and invoked telnet command to connect to stackoverflow.com and typed HEAD / HTTP/1.0<Enter twice> in the terminal. I got the following output from hook_sendmsg.stp:

root@debian:~# stap -g hook_sendmsg.stp
message block [0]; type=3(raw); state=1(unconnected)
14 00 00 00 16 00 01 03 ec 95 f4 52 00 00 00 00 |...........R....|
00 00 00 00                                     |................|
message block [0]; type=3(raw); state=1(unconnected)
14 00 00 00 16 00 01 03 ec 95 f4 52 00 00 00 00 |...........R....|
00 00 00 00                                     |................|
message block [0]; type=2(udp); state=1(unconnected)
4d 0d 01 00 00 01 00 00 00 00 00 00 0d 73 74 61 |M............sta|
63 6b 6f 76 65 72 66 6c 6f 77 03 63 6f 6d 00 00 |ckoverflow.com..|
01 00 01                                        |................|
message block [0]; type=2(udp); state=1(unconnected)
0f 1e 01 00 00 01 00 00 00 00 00 00 0d 73 74 61 |.............sta|
63 6b 6f 76 65 72 66 6c 6f 77 03 63 6f 6d 00 00 |ckoverflow.com..|
1c 00 01                                        |................|
message block [0]; type=1(tcp); state=3(connected)
48 45 41 44 20 2f 20 48 54 54 50 2f 31 2e 30 0d |HEAD / HTTP/1.0.|
0a                                              |................|
message block [0]; type=1(tcp); state=3(connected)
0d 0a                                           |................|

This shows that totally sock_sendmsg() were called 6 times in the context of telnet. Obviously the 3rd and 4th are DNS queries to Google's public DNS servers 8.8.8.8 and 8.8.4.4. The 5th and 6th are the two lines of HTTP request sent from telnet. But what were the 1st and 2nd called for? Did they called internally by the Kernel?

Thanks in advance.

  • You can add a print_backtrace(); print_ubacktrace(); pair to the probe handler to get a sense of what's going on. Add "-d /usr/bin/telnet --ldd -d kernel" to the stap command line to enable fuller backtracing. – fche Feb 07 '14 at 15:16
  • Thanks, fche. Useful tips for Kernel debugging. I tried those functions but `print_ubacktrace()` didn't work. It seems Debian doesn't enable `CONFIG_UTRACE` option by default. I have to recompile the Kernel. –  Feb 08 '14 at 01:06
  • Hm, I don't think you should need CONFIG_UPROBES just for print_ubacktrace(). If you're sure, please send us some details on the mailing list. – fche Feb 09 '14 at 05:00
  • Thanks. The following is the error message I got when I tried to insert `print_ubacktrace()`: `user-space facilities not available without kernel CONFIG_UTRACE`. In my environment `print_ubacktrace()` is defined in `tapset/ucontext-unwind.stp` and include `/* pragma:uprobes */`, so I'm pretty sure `print_ubacktrace()` needs `CONFIG_UPROBES`. –  Feb 10 '14 at 13:02
  • Right you are, Alice. We're investigating why that pragma thingie is still there. You could try removing it (to beat us to the testing). – fche Feb 10 '14 at 17:44
  • Yes. It worked without `pragma:uprobes`. It printed some addresses of instructions right after `syscall` function calls. Unlike `print_backtrace()`, it only printed addresses and not function names. –  Feb 11 '14 at 02:30
  • Add the "stap -d /usr/bin/telnet --ldd" options and try again. – fche Feb 11 '14 at 03:23
  • Thanks. It worked as expected on Fedora (Kernel 3.12 with uprobes enabled), but didn't on Debian (Kernel 3.2 with uprobes disabled). Does uprobes feature needed for `print_ubacktrace()` to work properly? BTW, when I invoked `stap` command on Fedora with those options, the following message was printed: `WARNING: Missing unwind data for module, rerun with 'stap -d /usr/lib64/libnss_dns-2.18.so'`. Doesn't `--ldd` options unwind shared libraries automatically? –  Feb 13 '14 at 01:57

1 Answers1

1

They are NETLINK messages, as you can see if you monitor the telnet command from userspace with strace (check the socket family):

sendto(3, "\24\0\0\0\26\0\1\3?\254\364R\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK,  pid=0, groups=00000000}, 12) = 20
sendto(3, "\24\0\0\0\26\0\1\3?\254\364R\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20
send(3, "\212\355\1\0\0\1\0\0\0\0\0\0\rstackoverflow\3com\0\0"..., 35, MSG_NOSIGNAL) = 35
send(3, "\241\353\1\0\0\1\0\0\0\0\0\0\rstackoverflow\3com\10i"..., 50, MSG_NOSIGNAL) = 50
send(3, "|\341\1\0\0\1\0\0\0\0\0\0\rstackoverflow\3com\0\0"..., 35, MSG_NOSIGNAL) = 35
send(3, "HEAD / HTTP/1.0\r\n", 17, 0)   = 17
send(3, "\r\n", 2, 0)                   = 2

Netlink is an interface used by the userspace processes to communicate with kernel (so, they are send by the telnet), usually for routing, firewalling... pourposes [1]. Some usual network C functions (i.e. getsockname) works internally with this kind of NETLINK messages.

If you want to investigate a bit more you should translate the payload of the send_message to the netlink header structure and check what type of messages are.:

       struct nlmsghdr {
           __u32 nlmsg_len;    /* Length of message including header. */
           __u16 nlmsg_type;   /* Type of message content. */
           __u16 nlmsg_flags;  /* Additional flags. */
           __u32 nlmsg_seq;    /* Sequence number. */
           __u32 nlmsg_pid;    /* Sender port ID. */
       };

[1]http://man7.org/linux/man-pages/man7/netlink.7.html

  • 1
    Specifically, glibc's getaddrinfo() uses its internal __check_pf() function to check whether ipv4 and/or ipv6 interfaces exist on the system. – fche Feb 07 '14 at 15:57
  • Thanks, Jon. I didn't know much about NETLINK, but it's interesting that NETLINK is used internally by some standard library functions. –  Feb 07 '14 at 17:31