top of page
  • Writer's picturekyle Hailey

Monitoring Application TCP traffic on Mac



photo by Stuart Williams

My internet provider said my service was degraded due to the large amount of data uploading from my computer. As far as I knew, my computer wasn’t uploading anything but I didn’t know how to prove it.

I decided to try and write a DTrace program to look at it. (I also installed “Little Snitch” which seems pretty cool).

One problem with using DTrace on TCP on a Mac is a lot of the providers and provider information I‘m use to on Solaris isn’t on the Mac. Another problem is that when receiving data, the program shows up as kernel_task instead of the program that the data was mean for. To get around this I did two things. One I recorded which program was using which IP when sending data and I also used some code from  a program by Brendan Gregg to track which programs connected on which IPs

#!/usr/sbin/dtrace -s
#pragma D option defaultargs
#pragma D option quiet
inline int af_inet = 2;         /* AF_INET defined in bsd/sys/socket.h */
inline int af_inet6 = 30;       /* AF_INET6 defined in bsd/sys/socket.h */

dtrace:::BEGIN
{      TITLE = 10;
       title = 0;
       walltime=timestamp;
       printf("starting up ...\n");
       procs["0"]=" ";
}

/* syscall::connect stuff from Brendan Gregg
   http://dtracebook.com/index.php/Network_Lower_Level_Protocols:soconnect.d#Mac_OS_X
*/
syscall::connect*:entry
{
        /* assume this is sockaddr_in until we can examine family */
        this->s = (struct sockaddr_in *)copyin(arg1, sizeof (struct sockaddr));
        this->f = this->s->sin_family;
}
syscall::connect*:entry
/ this->f == af_inet
/
{
        this->a = (uint8_t *)&this->s->sin_addr;
        this->addr1 = strjoin(lltostr(this->a[0] + 0ULL), strjoin(".",
            strjoin(lltostr(this->a[1] + 0ULL), ".")));
        this->addr2 = strjoin(lltostr(this->a[2] + 0ULL), strjoin(".",
            lltostr(this->a[3] + 0ULL)));
        self->address = strjoin(this->addr1, this->addr2);
        self->start = timestamp;
}
syscall::connect*:return
/self->start/
{
        procs[self->address]=execname;
        printf(" --> %-16s %s \n", execname, self->address );
        self->address = 0;
        self->start = 0;
}

tcp:::send, tcp:::receive
/   title == 0 /
{   printf("     %9s %8s %8s  \n",
        "delta"    ,
        "send" ,
        "recd"
      );
     title=TITLE;
}

tcp:::send
{    delta=timestamp-walltime;
     walltime=timestamp;
     printf("send %9d  %8d < /   %8s %-15s %s\n",         delta/1000,         args[2]->ip_plength - args[4]->tcp_offset,
        "",
        args[2]->ip_daddr       ,
        curpsinfo->pr_psargs
      );
     procs[args[2]->ip_daddr]=curpsinfo->pr_psargs;
     title--;
}

tcp:::receive
/   args[2]->ip_saddr   != "127.0.0.1"  && procs[args[2]->ip_saddr] == ""  /
{     delta=timestamp-walltime;
      walltime=timestamp;
      printf("recd %9d  %8s > \   %-8d %-15s %s (missing proc name) \n",
        delta/1000,
        "",
        args[2]->ip_plength - args[4]->tcp_offset,
        args[2]->ip_saddr       ,
        execname
      );
}

tcp:::receive
/   args[2]->ip_saddr   != "127.0.0.1"  && procs[args[2]->ip_saddr] != ""  /
{     delta=timestamp-walltime;
      walltime=timestamp;
      printf("recd %9d  %8s > \   %-8d %-15s %s\n",
        delta/1000,
        "",
        args[2]->ip_plength - args[4]->tcp_offset,
        args[2]->ip_saddr       ,
        procs[args[2]->ip_saddr]
      );
    title--;
}

The output looks like

 elapsed  send    recd  IP           program
send     34    49 < /      74.125.239.35 kernel_task
send  58276    49 < /      74.125.239.35 Safari
send  87468    49 < /      74.125.239.35 Safari
send 255594    49 < /      74.125.239.35 Safari
recd  46989       > \ 8241 74.125.239.35 Safari
send     69    49 < /      74.125.239.35 kernel_task
recd  36360       > \ 8113 74.125.239.53 WebProcess
send     28    49 < /      74.125.239.53 kernel_task
send   4751 65393 < /       74.125.20.84 WebProcess

Still missing some of the program names Would be nice if something like this would work

dtrace -n 'mib:::tcpInDataInorderBytes { @[execname] = sum(args[0]);}'

but mibs don’t seem to be implemented on the Mac.

So all in all “Little Snitch” seems much better, but it’s pay for package, and DTrace, at least as far as I have been able to use it, is a bit lacking.

I’m sure someone out there could but together a more useful DTrace TCP script for the Mac. Looking forward to any revelations people might have.

I once heard the quipped “you ask for hamburger and complain when I give you steak” when someone complained about not wanting to learn DTrace and just wanting to use a standard tool. I’d say instead of steak for hamburger it’s more like someone gives you a whole side of beef hanging on a hook when someone is just asking for a hamburger, or maybe like giving you the whole cattle when just wanting a hamburger. It’s often a lot of work to get to the hamburger, or steak, from there, though with some insight, especially into the kernel code, one can do amazing things.

0 views0 comments

Comments


bottom of page