views:

30

answers:

2

My scenario, I'm collecting network packets and if packets match a network filter I want to record the time difference between consecutive packets, this last part is the part that doesn't work. My problem is that I cant get accurate sub-second measurements no matter what C timer function I use. I've tried: gettimeofday(), clock_gettime(), and clock().

I'm looking for assistance to figure out why my timing code isn't working properly.

I'm running on a cygwin environment. Compile Options: gcc -Wall capture.c -o capture -lwpcap -lrt

Code snippet :

/*globals*/
int first_time = 0;
struct timespec start, end;
double sec_diff = 0;

main() {
  pcap_t *adhandle;
  const struct pcap_pkthdr header;
  const u_char *packet;
  int sockfd = socket(PF_INET, SOCK_STREAM, 0);

      .... (previous I create socket/connect - works fine)

  save_attr = tty_set_raw();

  while (1) {  

    packet = pcap_next(adhandle, &header);     // Receive a packet? Process it
    if (packet != NULL) {
      got_packet(&header, packet, adhandle);
    }

    if (linux_kbhit()) {         // User types message to channel
      kb_char = linux_getch();       // Get user-supplied character
      if (kb_char == 0x03)         // Stop loop (exit channel) if user hits Ctrl+C
        break;
    }
  } 
  tty_restore(save_attr);
  close(sockfd);
  pcap_close(adhandle);
  printf("\nCapture complete.\n");
}

In got_packet:

got_packet(const struct pcap_pkthdr *header, const u_char *packet, pcap_t * p){    ... {

   ....do some packet filtering to only handle my packets, set match = 1
  if (match == 1) {
    if (first_time == 0) {
      clock_gettime( CLOCK_MONOTONIC, &start );
      first_time++;
    }
    else {
      clock_gettime( CLOCK_MONOTONIC, &end );

      sec_diff = (end.tv_sec - start.tv_sec) + ((end.tv_nsec - start.tv_nsec)/1000000000.0);  // Packet difference in seconds

      printf("sec_diff: %ld,\tstart_nsec: %ld,\tend_nsec: %ld\n", (end.tv_sec - start.tv_sec), start.tv_nsec, end.tv_nsec);
      printf("sec_diffcalc: %ld,\tstart_sec: %ld,\tend_sec: %ld\n", sec_diff, start.tv_sec, end.tv_sec);
      start = end;         // Set the current to the start for next match
    }
  }
}

I record all packets with Wireshark to compare, so I expect the difference in my timer to be the same as Wireshark's, however that is never the case. My output for tv_sec will be correct, however tv_nsec is not even close. Say there is a 0.5 second difference in wireshark, my timer will say there is a 1.999989728 second difference.

A: 

I don't think that it is the clocks that are your problem, but the way that you are waiting on new data. You should use a polling function to see when you have new data from either the socket or from the keyboard. This will allow your program to sleep when there is no new data for it to process. This is likely to make the operating system be nicer to your program when it does have data to process and schedule it quicker. This also allows you to quit the program without having to wait for the next packet to come in. Alternately you could attempt to run your program at really high or real time priority.

You should consider getting the current time at the first instance after you get a packet if the filtering can take very long. You may also want to consider multiple threads for this program if you are trying to capture data on a fast and busy network. Especially if you have more than one processor, but since you are doing some pritnfs which may block. I noticed you had a function to set a tty to raw mode, which I assume is the standard output tty. If you are actually using a serial terminal that could slow things down a lot, but standard out to a xterm can also be slow. You may want to consider setting stdout to fully buffered rather than line buffered. This should speed up the output. (man setvbuf)

nategoose
Correct me if I'm wrong, but I thought my main while statement is doing the polling you describe, I see if a packet arrives or if the user inputs the quit character then act on it. How could I do this with less resource usage?
Shawn
polling functions, like `poll`, `select, and `epoll` put your program to sleep while it waits for new input. Linux (linux_kbhit gave away what OS you were using) will sometimes punish programs for hogging the CPU by knocking down its dynamic priority and/or boosting the dynamic priority of processes that do a lot of sleeping (which happens when you are waiting for IO or call `pause()` or `nanosleep()`).
nategoose
A: 

Basically, you will want to use a timer with a higher resolution

Also, I did not check in libpcap, but I am pretty sure that libpcap can give you the time at which each packet was received. In which case, it will be closest that you can get to what Wireshark displays.

The Stacker
Turns out, you are right - libpcap does handle it. In my got_packet function I pass const struct pcap_pkthdr *header which contains sec and usec resolution when the packet was received. I call it with: header->ts.tv_sec,header->ts.tv_usec and it gives me the best timing yet.
Shawn