pcap程式設計深入解析
首先,該文章不限定OS不單針對Winpcap或者libpcap,對於Winpcap的特殊擴充套件使用了PS標示。
我以前使用過Winpcap,但並沒有對pcap開發進行過深入解析。希望這篇文章可以深入淺出的講清楚這個問題。Pcap是什麼?它是個監視器,或者發生器,它可以監視,也可以發生,但它不能攔截。它可以在網路上增加一些訊息,但不能阻止已經存在的訊息。
獲取裝置列表:
intpcap_findalldevs (pcap_if_t **alldevsp, char *errbuf)
構造一個可開啟的網路裝置的列表
-1表示錯誤,buf中會有錯誤訊息;0表示成功。能被查詢到的裝置是能被開啟的裝置。
PS:下面這個函式是Windows平臺下的擴充套件,不是非常建議使用這個函式。
int pcap_findalldevs_ex (char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf)
建立一個網路裝置列表,它們可以由 pcap_open()開啟。
對比上面的函式,這個擴充套件表現在引數上,從上面可以看出來,前面兩個引數是增加的,那麼這個增加的引數就使開啟查詢遠端網絡卡成為可能。這兩個引數的規則,請檢視Winpcap相關文件。
開啟裝置
pcap_t *pcap_open_live (const char *device, int snaplen, int promisc, int to_ms, char *ebuf)
在網路中開啟一個活動的捕獲
第一個引數是從查詢查到的裝置名,snaplen為最大抓包長度,65535可以滿足大多數的應用,promisc代表開啟裝置的模式,當選擇混雜模式的情況下,可以抓下所有的包。To_ms,抓包的超時時間。
PS:Winpcap擴充套件介面
pcap_t * pcap_open (const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf)
開啟一個用來捕獲或傳送流量(僅WinPcap)的通用源。
開啟一個檔案
pcap_t * pcap_open_offline (const char *fname, char *errbuf)
開啟一個 tcpdump/libpcap 格式的儲存檔案,來讀取資料包。
過濾器的使用
int pcap_compile (pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
編譯資料包過濾器,將程式中高階的過濾表示式,轉換成能被核心級的過濾引擎所處理的東西。 (參見 過濾表示式語法)第一個引數代表裝置控制代碼,第二個引數為輸出引數,為編譯完成的過濾條件,第三個引數代表過濾條件的表示式,第四個引數表示是否優化,1是個不錯的選擇,第五個引數為網絡卡的掩碼。
int pcap_setfilter (pcap_t *p, struct bpf_program *fp)
在捕獲過程中繫結一個過濾器
回撥抓包
int pcap_dispatch (pcap_t *p, int cnt, pcap_handler callback, u_char *user)
收集一組資料包
int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user)
收集一組資料包
typedef void(*) pcap_handler (u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)
接受資料包的回撥函式的原型
當介面卡被開啟,捕獲工作就可以用 pcap_dispatch() 或 pcap_loop()進行。 這兩個函式非常的相似,區別就是 pcap_ dispatch() 當超時時間到了(timeout expires)就返回 (儘管不能保證) ,而 pcap_loop() 不會因此而返回,只有當 cnt 資料包被捕獲,所以,pcap_loop()會在一小段時間內,阻塞網路的利用。pcap_loop()對於我們這個簡單的範例來說,可以滿足需求,不過, pcap_dispatch() 函式一般用於比較複雜的程式中。
這兩個函式都有一個 回撥 引數, packet_handler指向一個可以接收資料包的函式。 這個函式會在收到每個新的資料包並收到一個通用狀態時被libpcap所呼叫 ( 與函式 pcap_loop() 和 pcap_dispatch() 中的 user 引數相似),資料包的首部一般有一些諸如時間戳,資料包長度的資訊,還有包含了協議首部的實際資料。 注意:冗餘校驗碼CRC不再支援,因為幀到達介面卡,並經過校驗確認以後,介面卡就會將CRC刪除,與此同時,大部分介面卡會直接丟棄CRC錯誤的資料包,所以,Pcap沒法捕獲到它們。
非回撥抓包
u_char * pcap_next (pcap_t *p, struct pcap_pkthdr *h)
返回下一個可用的資料包
int pcap_next_ex (pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data)
從一個裝置介面,或從一個離線檔案中,讀取一個數據包
第一個引數代表裝置控制代碼,第二個引數代表包頭,第三個引數代表資料。從我對引數的理解來看,無論是回撥或者非回撥,記憶體都是pcap管理的,且這些記憶體是複用的,沒有任何證據表明pcap_next_ex這類的介面是執行緒安全的,至少引數不滿足執行緒安全的要求。所以我理解的抓包都需要在單執行緒下進行。如果需要延遲分析的話,需要把所有的資料另行快取,pcap是不會做這些工作的。
對比之下,為什麼我們要用 pcap_next_ex() 代替以前的 pcap_next()? 因為 pcap_next() 有一些不好的地方。首先,它效率低下,儘管它隱藏了回撥的方式,但它依然依賴於函式 pcap_dispatch()。第二,它不能檢測到檔案末尾這個狀態(EOF),因此,如果資料包是從檔案讀取來的,那麼它就不那麼有用了。
傳送資料包
傳送單個數據包
int pcap_sendpacket (pcap_t *p, u_char *buf, int size)
傳送一個原始資料包
第一個引數為裝置控制代碼,第二個資料為快取,第三個資料為buffer的大小。
統計
int pcap_stats (pcap_t *p, struct pcap_stat *ps)
返回當前捕獲的統計資訊
PS:對Winpcap,對於統計來說,pcap使用pcap_setmode直接將模式設定到驅動層,通過驅動層實現對統計的優化。
關閉裝置
pcap_close
關閉查詢的裝置
pcap_freealldevs
pcap的底層實現
pcap在我們看來非常底層,其實並不是這樣。Pcap對網絡卡的呼叫,以來Cace technologies的packet.dll。而packet.dll的實現使用的是windows底層API——DeviceIOControl。
相關資源: