2012-07-26 26 views
0

昨天,我問我是否在asynchronous use of libpcap was making me lose packets。今天,我看得更遠,似乎問題不在於libpcap的異步使用,而在於使用pcap_next_ex。有時候(10個用完1000),pcap_next_ex將在pcap句柄超時過期之前返回,告訴程序沒有要讀取的數據包(即使它們在那裏)。pcap_next偶爾在Linux上丟失數據包

以下驗證概念再現了這個問題。它取決於libpcap,pthread,boost和libcrafter(一個漂亮的C++數據包製作庫)。基本上,它會向目標發送一組TCP-SYN數據包,並嘗試使用libpcap獲取響應。一個調用pcap_loop的線程是並行運行的 - 當主程序錯過某些響應(如上所述)時,該線程捕獲所有數據包。

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <boost/format.hpp> 
#include <boost/make_shared.hpp> 
#include <boost/shared_ptr.hpp> 

#include <crafter.h> 
#include <netinet/ip.h> 
#include <netinet/tcp.h> 
#include <pcap.h> 
#include <sys/types.h> 
#include <sys/socket.h> 

using namespace boost; 
using namespace Crafter; 
using namespace std; 

int captureThreadCount = 0; 

typedef vector<pair<shared_ptr<Packet>, posix_time::time_duration> > PacketTimestamp; 
PacketTimestamp capturedThreadPackets; 

void captureThreadCallback(u_char* user, const struct pcap_pkthdr* h, const u_char* bytes) 
{ 
    shared_ptr<Packet> packet = make_shared<Packet>(); 
    packet->PacketFromIP(bytes + 16, h->caplen - 16); 
    posix_time::time_duration timestamp = posix_time::seconds(h->ts.tv_sec) + 
     posix_time::microseconds(h->ts.tv_usec); 
    capturedThreadPackets.push_back(make_pair(packet, timestamp)); 
    ++captureThreadCount; 
} 

void* captureThread(void* arg) 
{ 
    pcap_t* pcap = (pcap_t*) arg; 
    pcap_loop(pcap, -1, captureThreadCallback, NULL); 
} 

int main(int argc, char* argv[]) 
{ 
    if (argc != 5) { 
     cout << "Usage: " << argv[0] << " <source ip> <destination ip> <port> <# tries>" << endl; 
     exit(1); 
    } 

    InitCrafter(); 

    // Parameters. 
    string sourceIp = argv[1]; 
    string destinationIp = argv[2]; 
    int port = atoi(argv[3]); 
    int nTries = atoi(argv[4]); 

    char errorBuffer[1024]; 
    // Socket for sending, 
    int sd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); 
    // And sockaddr_in to send to. 
    struct sockaddr_in sin; 
    sin.sin_family = AF_INET; 
    sin.sin_port = htons(port); 
    sin.sin_addr.s_addr = inet_addr(destinationIp.c_str());  
    // One pcap for the main thread (calling pcap_next), 
    pcap_t* pcapForNext = pcap_open_live(NULL, BUFSIZ, false, 1000, errorBuffer); 
    // Another pcap for a capture thread (calling pcap_loop), 
    pcap_t* pcapCapture = pcap_open_live(NULL, BUFSIZ, false, 1000, errorBuffer); 
    // Both filtered for SYN+ACK or RST+ACK from destination:port to source. 
    string filterExpression = (boost::format("ip src %s and dst %s and src port %d and ((tcp[tcpflags] & (tcp-syn|tcp-ack) != 0) or (tcp[tcpflags] & (tcp-rst|tcp-ack) != 0))") % destinationIp % sourceIp % port).str(); 
    struct bpf_program filter; 
    pcap_compile(pcapForNext, &filter, filterExpression.c_str(), false, 0); 
    pcap_setfilter(pcapForNext, &filter); 
    pcap_setfilter(pcapCapture, &filter); 
    pcap_freecode(&filter); 
    // Don't forget the capture thread! 
    pthread_t thread; 
    pthread_create(&thread, NULL, captureThread, pcapCapture); 

    // Some statistics. 
    int packetsSent = 0; 
    int packetsReceived = 0; 
    int packetsTimeout = 0; 
    int packetsFailed = 0; 

    // Let's probe. 
    for (int i = 0; i < nTries; ++i) { 
     // Create packet, 
     IP ipHeader; 
     ipHeader.SetSourceIP(sourceIp); 
     ipHeader.SetDestinationIP(destinationIp); 
     TCP tcpHeader; 
     tcpHeader.SetSrcPort(12345 + i); 
     tcpHeader.SetDstPort(port); 
     tcpHeader.SetFlags(TCP::SYN); 
     shared_ptr<Packet> packet = make_shared<Packet>(ipHeader/tcpHeader); 
     // Check the time, 
     struct timeval tv; 
     gettimeofday(&tv, NULL); 
     posix_time::time_duration sentTime = 
      posix_time::seconds(tv.tv_sec) + posix_time::microseconds(tv.tv_usec); 
     // And send it. 
     if (packet->SocketSend(sd) > 0) { 
      cerr << "Sent packet " << i << " at " << sentTime << "." << endl; 
      ++packetsSent; 
     } 
     else { 
      cerr << "Sending packet " << i << " failed." << endl; 
      continue; 
     } 

     struct pcap_pkthdr* pktHeader; 
     const u_char* pktData; 
     int r; 
     // Wait for the response. 
     if ((r = pcap_next_ex(pcapForNext, &pktHeader, &pktData)) > 0) { 
      posix_time::time_duration timestamp = 
       posix_time::seconds(pktHeader->ts.tv_sec) + 
       posix_time::microseconds(pktHeader->ts.tv_usec); 
      cerr << "Response " << i << " received at " << timestamp << "." << endl; 
      ++packetsReceived; 
     } 
     else if (r == 0) { 
      cerr << "Timeout receiving response for " << i << "." << endl; 
      ++packetsTimeout; 
     } 
     else { 
      cerr << "Failed receiving response for " << i << "." << endl; 
      ++packetsFailed; 
     } 
    } 

    // Wait (to ensure "fast packets" are captured by the capture thread), 
    usleep(500000); // 500 ms. 

    for (PacketTimestamp::iterator i = capturedThreadPackets.begin(); 
     i != capturedThreadPackets.end(); ++i) { 
     TCP* tcpLayer = GetTCP(*i->first); 
     cout << "Captured packet " << (tcpLayer->GetDstPort() - 12345) << " at " << i->second << "." << endl; 
    } 

    cout << "SNT=" << packetsSent << 
     ", RCV=" << packetsReceived << 
     ", TIM=" << packetsTimeout << 
     ", FLD=" << packetsFailed << 
     ", CAP=" << captureThreadCount << "." << endl; 

    CleanCrafter(); 
} 

回答

0

這似乎是使用Linux中內存映射(它發生甚至在最新的libpcap,1.3.0)libpcap的一個問題。如果libpcap編譯時沒有內存映射支持(如message from the tcpdump mailing list archive中所述),問題就會消失。

+0

這也報告給pcap開發者:https://sourceforge.net/tracker/?func=detail&aid=3548076&group_id=53067&atid=469577。 – 2012-07-30 23:21:44

+0

而這被轉移到[libpcap GitHub網站上的問題](https://github.com/the-tcpdump-group/libpcap/issues/169)。 – 2015-03-26 21:16:47