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