1. 程式人生 > >pcap程式設計深入解析

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,抓包的超時時間。

PSWinpcap擴充套件介面

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 technologiespacket.dll。而packet.dll的實現使用的是windows底層API——DeviceIOControl

相關資源: