2012-07-26 4 views
0

어제, 내 질문에 내 asynchronous use of libpcap was making me lose packets. 오늘 나는 더 멀리 바라 보았고 문제는 libpcap의 비동기 적 사용이 아니라, pcap_next_ex의 사용에 관한 것으로 보인다. 가끔씩 (1000이 10을 벗어나면), pcap_next_ex은 pcap 처리 제한 시간이 만료되기 전에 돌아가서 읽을 패킷이 없다는 것을 프로그램에 알립니다.가끔씩 pcap_next가 리눅스에서 패킷을 잃음

다음 증명 개념은이 문제를 재현합니다. 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에 문제가있는 것 같습니다 (최신 libpcap 1.3.0에서도 발생합니다). libpcap이 메모리 매핑 지원없이 컴파일 된 경우 (message from the tcpdump mailing list archive에서 설명) 문제가 사라집니다.

+0

이것은 또한 pcap 개발자에게보고되었습니다. https://sourceforge.net/tracker/?func=detail&aid=3548076&group_id=53067&atid=469577 –

+0

그리고 그것은 [libpcap GitHub 사이트의 문제] (https://github.com/the-tcpdump-group/libpcap/issues/169)로 옮겨졌습니다. –

관련 문제