1. 程式人生 > >WinPcap學習(四)開啟介面卡並捕獲資料包

WinPcap學習(四)開啟介面卡並捕獲資料包

開啟裝置的函式是pcap_open()。下面引數snaplen,flags和to_ms的解釋說明

snaplen制定要捕獲資料包中的哪些部分。在一些作業系統中(比如xBSD和Win32),驅動可以被配置成只捕獲資料包的初始化部分:這樣可以減少應用間程式間複製資料的量,從而提高捕獲效率。本例中,我們將值定為65535,它比我們能遇到的最大的MTU達要大。因此,我們確信我們總收到完整的資料包。

flags:最最重要的flag是用來指示介面卡是否要補設定成混雜模式。一般情況下,介面卡只接收發送給它自己的資料包,而那些在其他機器之間通訊的資料包,將會被丟棄。相反,如果介面卡是混雜模式,那麼不管這個資料包是不是傳送給我的,我都會去捕獲。這意味著在一個共享媒介(比如匯流排型乙太網),WinPcap能捕獲其他主機的所有資料包。大多數用於資料捕獲的應用程式都會將介面卡設定混雜模式,所以,我們也會在下面使用混雜模式。

to_ms:指定讀取資料的超時時間,以毫秒計。在介面卡上進行讀取操作(比如用pcap_dispatch()或pcap_next_ex())都會在to_ms毫秒時間內響應,即使在網路上沒有可用的資料包。在統計模式下,to_ms還可以用來定義統計的時間間隔。將to_ms設定為0意味著沒有超進,那麼如果沒有資料包到達的話,讀操作將永遠不會返回。如果設定成-1,名家跟個部恰好相反,無論有沒有資料包到達,讀操作都會立即返回。

#include<iostream>
#include<pcap.h>

//packet handler函式原型
void packet_handler(u_char *param,const pcap_pkthdr *header,const u_char *pkt_data);

int main()
{
    pcap_if_t *alldevs;
    char errbuf[PCAP_ERRBUF_SIZE];
    //獲取本機裝置列表
    if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&alldevs,errbuf)==-1)
    {
        std::cerr<<"Error in pcap_findalldevs:"<<errbuf<<std::endl;
        exit(1);
    }
    //列印列表
    int i=0;
    pcap_if_t *d;
    for(d=alldevs;d;d=d->next)
    {
        std::cout<<++i<<"."<<d->name<<std::endl;
        if(d->description)
            std::cout<<d->description<<std::endl;
        else
            std::cout<<"(No description available)"<<std::endl;
    }
    if(i==0)
    {
        std::cout<<"\nNo interface found!Make sure WinPcap is installed."<<std::endl;
        return -1;
    }
    std::cout<<"Enter the interface number(1-"<<i<<")";
    int inum;
    std::cin>>inum;
    if(inum<1||inum>i)
    {
        std::cout<<"\nInterface number out of range."<<std::endl;
        pcap_freealldevs(alldevs);
        return -1;
    }
    //跳轉到選中的介面卡
    for(d=alldevs,i=0;i<inum-1;d=d->next,++i);

    //開啟裝置
    pcap_t *adhandle;
    if((adhandle=pcap_open(d->name,                     //裝置名
                           65536,
                           PCAP_OPENFLAG_PROMISCUOUS,   //混雜模式
                           1000,                        //讀取超時時間
                           NULL,                        //遠端機器驗證
                           errbuf                       //錯誤緩衝池
                           ))==NULL)
    {
        std::cerr<<"\nUnable to open the adapter."<<d->name<<" is not supported by WinPcap"<<std::endl;
        pcap_freealldevs(alldevs);
        return -1;
    }
    std::cout<<"\nlistening on "<<d->description<<"..."<<std::endl;
    pcap_freealldevs(alldevs);
    //開始捕獲
    pcap_loop(adhandle,0,packet_handler,NULL);

    return 0;
}
//每次捕到資料包時,libcap都會自動呼叫這個回撥函式
void packet_handler(u_char *param,const pcap_pkthdr* header,const u_char *pkt_data)
{
    tm* ltime;
    char timestr[16];
    time_t local_tv_sec;

    // 將時間戳轉換成可識別的格式
    local_tv_sec = header->ts.tv_sec;
    ltime = localtime(&local_tv_sec);
    strftime(timestr,sizeof timestr,"%H:%M:%S",ltime);
    printf("%s,%.6d len:%d\n",timestr,header->ts.tv_usec,header->len);
}


當介面卡被開啟,捕獲工作就可以用pcap_dispatch()或pcap_loop()進行。這兩個函式非常的相似,區別是pcap_dispatch()當超時到了就返回,而pcap_loop()不會因此返回,只有當cnt資料包被捕獲,所以pcap_loop()會在一小段時間內阻塞網路的利用。

這兩個函式都有一個回撥函式,packet_handler指向一個可以接收資料包的函式。這個函式會在收到每個新的資料包並收到一個通用狀態時被libpcap所呼叫,資料包的首部一般有一些如時間戳,資料包長度的資訊,還包含了協議首部的實際資料。注意:冗餘檢驗碼CRC不再支援,因為幀到達介面卡,並經過檢驗確認以,介面卡就會將CRC刪除,與此同時,大部分介面卡會直接丟棄CRC錯誤的資料包,所以,WinPcap沒法捕獲到它們。

上面的程式將每一個數據包的時間戳和長度從 pcap_pkthdr 的首部解析出來,並列印在螢幕上。

請注意,使用 pcap_loop() 函式可能會遇到障礙,主要因為它直接由資料包捕獲驅動所呼叫。因此,使用者程式是不能直接控制它的。另一個實現方法(也是提高可讀性的方法),是使用 pcap_next_ex()函式。

在IDE中試驗這個例程時,packet_handler最後一個引數忘加const,一直報錯,後來才記得:對於指標const是可以區分兩個函式的。

最後歡迎大家訪問我的個人網站: 1024s