Develop

Pcap Library를 이용한 Packet Capture

Dork94 2017. 10. 18. 04:39

네트워크 프로그래밍을 하다 보면 가장 기본이 되는 Packet Pacture방법에 대해서 알아 보도록 하겠습니다.


Packet을 Capture하는 방법에는 Netfilter, Raw Socket , Pcap등 다양한 방법이 있지만, 아마 그중에서도 가장 유명한 것이 Pcap이 아닐까 싶습니다.


그래서 오늘은 간단한 예제로 패킷을 캡쳐하고 사용하는 방법 및, 소스코드에 대해서 잠시 살펴 보도록 하겠습니다.


//
//  main.cpp
//  jpcapLib
//
//  Created by 장한빈 on 2017. 10. 18..
//  Copyright © 2017년 Dork. All rights reserved.
//

#include <iostream>
#include <pcap>

using namespace std;

#define NONPROMISCUOUS 0
#define PROMISCUOUS 1


bool recvPacket(pcap_t* pcd, uint8_t **packetData,int& dataLen);

int main(int argc, const char * argv[]) {
    
    char errBuffer[PCAP_ERRBUF_SIZE];
    uint8_t* packetData;
    int dataLen;
    pcap_t* pcd;
    
    
    if((pcd=pcap_open_live(pcap_lookupdev(errBuffer),BUFSIZ,NONPROMISCUOUS,1,errBuffer))==NULL)
        perror("pcap_open_live error! in Main.cpp");
    
    
    if(recvPacket(pcd, &packetData, dataLen))
    {
        cout<<"Packet Come in!!"<<endl;
        //packet saved in packetData
        //packet capture length saved in dataLen
        
        /***************example Code*****************/
        /*
         struct ether_header *ep= (struct ether_header*)packetData;
         */
        /***************example Code*****************/
    }
    return 0;
}



bool recvPacket(pcap_t* pcd, uint8_t **packetData,int& dataLen)
{
    
    const u_char *pkt_data;
    struct pcap_pkthdr *pktHeader;
    int valueOfNextEx;
    
    while(true)
    {
        valueOfNextEx=pcap_next_ex(pcd,&pktHeader,&pkt_data);
        
        switch (valueOfNextEx)
        {
            case 1:
                *packetData=(uint8_t*)pkt_data;
                dataLen=pktHeader->caplen;
                return true;
            case 0:
                cout<<"need a sec.. to packet capture"<<endl;
                continue;
            case -1:
                perror("pcap_next_ex function has an error!!");
                exit(1);
                
            case -2:
                cout<<"the packet have reached EOF!!"<<endl;
                exit(0);
            default:
                return false;
        }
    }
}



우선 위의 코드에서 pcd를 main에서 선언하게 되는데, 만약, 함수에서 pcd를 생성을 해준다면, 함수의 생성 및 호출 시 마다 pcd를 open 및 close 해주어야 하는 cost가 발생합니다. 때문에 pcd는 가급적 인자로 받도록 합니다.


또한, recvPacket의 인자중 2번째 인자를 2중포인터로 받는데 그 이유는 주소값을 값으로 취급하는(Capture된 Packet의 시작주소)변수를 사용해야하며 그 이유는 Capture된 Packet의 시작 주소를 값으로 넣어 Main에서 사용 할 수 있도록 하기 위함입니다. 또한 dataLen은 Capture된 Packet의 Length를 의미하며, 


pcap_pkthdr에 변수에 보면 len과 caplen 두가지가 있는데, 이때 len은 원본 Packet의 길이이며, caplen은 캡쳐된(프로그램에서 받은) Packet의 길이입니다.


따라서, Len을 쓴다면 만에 하나 오버플로우가 일어날 수 있으므로, caplen을 가급적 사용 바랍니다.


많은 블로그에서는 캡쳐시에 callback함수를 부르는 방법으로 pcap Packet Capture에 대해 서술하고 있는데 callback함수를 쓰는 것 보다는 next_ex함수를  loop로 돌려 해당 패킷을 사용자의 임의대로 유동적으로 handing하는 것이 보다 나은 방법 입니다.


그리고 마지막으로 recvPacket의 while문 안에서 if문의 반복 사용 보다는 switch문의 사용을 권장 합니다.


네트워크의 특성상 Packet의 capture가 빈번하게 발생하므로 if문을 사용한다면 해당 조건 비교를 위해 조건식이 맞을 때 까지 소스코드를 진행하며 찾아야 하지만, 


switch문을 사용한다면 해당 코드까지 한번에 jump할 수 있어 capture의 cost가 줄어들게 됩니다.


질문은 댓글로 받도록 하겠습니다.