Saturday, December 17, 2011

Replay Multicast

I tried tcpdump/tcpreplay to do multicast replay.

# snaplen = maximum udp payload size + udp header size + ip header size + ethernet header size
# need to tune -B -s carefully for packet lossless
tcpdump -B 10240 -s 2112 -tt -vv -i eth4 -w eth4.pcap multicast
tcpreplay -t -i lo eth4.pcap

However, tcpdump could capture packets from tcpreplay, my program failed to subscribe. I tried to tcpedit MAC per [Tcpreplay-users] Could not replay a multicast, but still did not work. After search for a while, I decided to write my own tcpreplay.

#include <err.h>
#include <pcap.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

// ethernet headers are always exactly 14 bytes [1]
#define SIZE_ETHERNET 14 // http://www.tcpdump.org/pcap.html
void replayOnePcap(pcap_t* pcap, long offset, int fd) 
{  
  struct sockaddr_in addr;
  bzero((char *)&addr, sizeof(addr));
  addr.sin_family = AF_INET;

  struct pcap_pkthdr header;
  const u_char* packet;
  long num = 0;
  long vol = 0;
  time_t lastReportTm;
  while ((packet = pcap_next(pcap, &header)) != NULL) {
    const struct ip* ip = (struct ip*)(packet + SIZE_ETHERNET);
    if (*((const u_char*)ip + 16) < 224) // check if it is multicast
      continue;
    assert(ip->ip_p == IPPROTO_UDP);
    timeval now;
    time_t t;
    for (;;) {
      gettimeofday(&now, NULL);
      t = now.tv_sec+offset;
      if (header.ts.tv_sec < t || 
          (header.ts.tv_sec == t && header.ts.tv_usec <= now.tv_usec))
        break;
      //usleep(1);
    }   
    int size_ip = (ip->ip_hl)*4;
    const struct udphdr* udp = (struct udphdr*)(packet + SIZE_ETHERNET + size_ip);
    int n = SIZE_ETHERNET + size_ip + sizeof(udphdr);
    const u_char* payload = packet + n;
    addr.sin_addr.s_addr = ip->ip_dst.s_addr;
    addr.sin_port = udp->dest;
    ++num;
    vol += sizeof(header) + header.len;
    ::sendto(fd, payload, header.len - n, 0, (struct sockaddr*)&addr, sizeof(addr));
    if (now.tv_sec - lastReportTm >= 10) {
      lastReportTm = now.tv_sec;
      LOG_INFO('#' << num/1e+6 << "M " << vol/1e+6 << "M : " <<
          header.ts.tv_usec << "us " << asctime(localtime(&header.ts.tv_sec)));
      LOG_INFO("expect: " << asctime(localtime(&t)));
    }
  }
}

No comments:

Post a Comment