0

I created a c++ program that reads and analyzes pcap files via fread function.

It is a program that reads the global header of pcap file, reads the pcap header, and extracts a specific field value by specifying each protocol header structure. Compared to tcpdump tool, tcpdump read the pcap file about three times faster. My program is really simple, but I don't know why it is slow. What do I need to do to improve my program's performance?

The following is my code. Note that the type of result(Variable) is ostringstream. and The Program is work by calling analyze() repeatly to read all the pcap files.

PcapAnalyzer::PcapAnalyzer(const std::string& filename)
{
  PcapAnalyzerfile = fopen("test.pcap", "r");
  if (PcapAnalyzerfile == nullptr)
    throw std::runtime_error("Failed to read Pcap");

  struct PcapAnalyzer_file_header pfh;
  if (fread(&pfh, sizeof(pfh), 1, PcapAnalyzerfile) < 1)
    throw std::runtime_error("Failed to read Pcap");

  if(pfh.linktype != 1)
     throw std::runtime_error("No Support");
}

std::string PcapAnalyzer::analyze()
{
  int process_len = 0;
  int packet_len = 0;

  result.str("");
  result.clear();
  packet_len = PcapAnalyzer_header();
  if (packet_len == -1)
    return {};
  process_len = ethernet();
  if (process_len == -1)
    throw std::runtime_error("failed to analyze");
  packet_len -= process_len;
  if (!payload(packet_len))
    throw std::runtime_error("failed to analyze");

  return result.str();
}

int PcapAnalyzer::PcapAnalyzer_header()
{
  struct PcapAnalyzer_pkthdr pp;
  char* cap_time;
  long sec;
  if (fread(&pp, sizeof(pp), 1, PcapAnalyzerfile) < 1)
    return -1;
  sec = (long)pp.ts.tv_sec;
  cap_time = (char*)ctime((const time_t*)&sec);
  cap_time[strlen(cap_time) - 1] = '\0';
  result << std::dec;
  result << pp.len << " ";
  result << cap_time << " ";

  return pp.caplen;
}

int PcapAnalyzer::ethernet()
{
  struct ether_header eh;
  int process_len = 0;

  if (fread(&eh, sizeof(eh), 1, PcapAnalyzerfile) < 1)
    return -1;
  process_len += sizeof(eh);

  if(htons(eh.ether_type) != ETHERTYPE_IP)
    return process_len;

  process_len += ipv4();
  if (process_len == -1)
    throw std::runtime_error("failed to analyze");
  return process_len;
}

int PcapAnalyzer::ipv4()
{
  struct ip iph;
  int opt = 0;
  int process_len = 0;

  if (fread(&iph, 20, 1, PcapAnalyzerfile) < 1)
    return -1;
  result << IP_V(iph.ip_vhl) << " ";
  result << IP_HL(iph.ip_vhl) << " ";
  result << iph.ip_tos << " ";
  result << iph.ip_len << " ";
  result << iph.ip_id << " ";
  result << iph.ip_off << " ";
  result << iph.ip_ttl << " ";

  process_len += 20;
  opt = IP_HL(iph.ip_vhl) * 4 - sizeof(iph);
  if (opt != 0) {
    fseek(PcapAnalyzerfile, opt, SEEK_CUR);
    process_len += opt;
    result << "ip_opt ";
  }
  if(iph.ip_p != IPPROTO_UDP)
    return process_len;

  process_len += udp();
  if (process_len == -1)
    throw std::runtime_error("failed to read transport header");
  return process_len;
}

int PcapAnalyzer::udp()
{
  struct udphdr udph;
  int process_len = 0;
  if (fread(&udph, sizeof(udph), 1, PcapAnalyzerfile) < 1)
    return -1;
  process_len += sizeof(udph);

  result << ntohs(udph.uh_sport) << " ";
  result << ntohs(udph.uh_dport) << " ";
  result << ntohs(udph.uh_ulen) << " ";
  result << ntohs(udph.uh_sum) << " ";

  return process_len;
}

bool PcapAnalyzer::payload(int remain_len)
{
  if (remain_len == 0)
    return true;
  char buffer[remain_len];
  if (fread(buffer, remain_len, 1, PcapAnalyzerfile) < 1)
    return false;

  return true;
}
김현우
  • 23
  • 6
  • Try reading the entire file into memory in one big chunk, then parse it in-memory, rather than reading it in multiple small chunks. Or maybe try memory mapping it. – Jesper Juhl Aug 11 '18 at 12:21
  • 4
    Most common performance issue is that people measure performance of debug versions of programs that have lot of run-time checks injected and optimizations turned off. People who do not do that mistake usually also know how to use profilers. – Öö Tiib Aug 11 '18 at 12:21
  • Have you considered reading the source code for libpcap? It is open source, after all. – David Hoelzer Aug 13 '18 at 11:21
  • All your file functions seem to be part of < cstdio >. You might try submitting a variant of this code (i.e. with no class thingies) with the c tag. Those programmers have talent and practice with FILE*. On the other hand, to begin your odyssey into C++, you might take a look at what is commonly referred to as 'slurp' (which in 1 or 2 lines reads a std::fstream into a std::string) https://stackoverflow.com/q/116038/2785528. C++ uses streams (not FILE*). – 2785528 Aug 14 '18 at 12:12

0 Answers0