linux-pcap 抓包程序框架
生活随笔
收集整理的這篇文章主要介紹了
linux-pcap 抓包程序框架
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
轉(zhuǎn):http://blog.chinaunix.net/uid-21556133-id-120228.html
?
libpcap詳解 2010-12-01 22:07 libpcap(Packet Capture Library),即數(shù)據(jù)包捕獲函數(shù)庫(kù),是Unix/Linux平臺(tái)下的網(wǎng)絡(luò)數(shù)據(jù)包捕獲函數(shù)庫(kù)。它是一個(gè)獨(dú)立于系統(tǒng)的用戶層包捕獲的API接口,為底層網(wǎng)絡(luò)監(jiān)測(cè)提供了一個(gè)可移植的框架。 一、libpcap工作原理 libpcap主要由兩部份組成:網(wǎng)絡(luò)分接頭(Network Tap)和數(shù)據(jù)過(guò)濾器(Packet Filter)。網(wǎng)絡(luò)分接頭從網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序中收集數(shù)據(jù)拷貝,過(guò)濾器決定是否接收該數(shù)據(jù)包。Libpcap利用BSD Packet Filter(BPF)算法對(duì)網(wǎng)卡接收到的鏈路層數(shù)據(jù)包進(jìn)行過(guò)濾。BPF算法的基本思想是在有BPF監(jiān)聽(tīng)的網(wǎng)絡(luò)中,網(wǎng)卡驅(qū)動(dòng)將接收到的數(shù)據(jù)包復(fù)制一份交給 BPF過(guò)濾器,過(guò)濾器根據(jù)用戶定義的規(guī)則決定是否接收此數(shù)據(jù)包以及需要拷貝該數(shù)據(jù)包的那些內(nèi)容,然后將過(guò)濾后的數(shù)據(jù)給與過(guò)濾器相關(guān)聯(lián)的上層應(yīng)用程序。 libpcap的包捕獲機(jī)制就是在數(shù)據(jù)鏈路層加一個(gè)旁路處理。當(dāng)一個(gè)數(shù)據(jù)包到達(dá)網(wǎng)絡(luò)接口時(shí),libpcap首先利用已經(jīng)創(chuàng)建的Socket從鏈 路層驅(qū)動(dòng)程序中獲得該數(shù)據(jù)包的拷貝,再通過(guò)Tap函數(shù)將數(shù)據(jù)包發(fā)給BPF過(guò)濾器。BPF過(guò)濾器根據(jù)用戶已經(jīng)定義好的過(guò)濾規(guī)則對(duì)數(shù)據(jù)包進(jìn)行逐一匹配,匹配成 功則放入內(nèi)核緩沖區(qū),并傳遞給用戶緩沖區(qū),匹配失敗則直接丟棄。如果沒(méi)有設(shè)置過(guò)濾規(guī)則,所有數(shù)據(jù)包都將放入內(nèi)核緩沖區(qū),并傳遞給用戶層緩沖區(qū)。 二、libpcap的抓包框架 pcap_lookupdev()函數(shù)用于查找網(wǎng)絡(luò)設(shè)備,返回可被pcap_open_live()函數(shù)調(diào)用的網(wǎng)絡(luò)設(shè)備名指針。 pcap_open_live()函數(shù)用于打開(kāi)網(wǎng)絡(luò)設(shè)備,并且返回用于捕獲網(wǎng)絡(luò)數(shù)據(jù)包的數(shù)據(jù)包捕獲描述字。對(duì)于此網(wǎng)絡(luò)設(shè)備的操作都要基于此網(wǎng)絡(luò)設(shè)備描述字。 pcap_lookupnet()函數(shù)獲得指定網(wǎng)絡(luò)設(shè)備的網(wǎng)絡(luò)號(hào)和掩碼。 pcap_compile()函數(shù)用于將用戶制定的過(guò)濾策略編譯到過(guò)濾程序中。 pcap_setfilter()函數(shù)用于設(shè)置過(guò)濾器。 pcap_loop()函數(shù)pcap_dispatch()函數(shù)用于捕獲數(shù)據(jù)包,捕獲后還可以進(jìn)行處理,此外pcap_next()和pcap_next_ex()兩個(gè)函數(shù)也可以用來(lái)捕獲數(shù)據(jù)包。 pcap_close()函數(shù)用于關(guān)閉網(wǎng)絡(luò)設(shè)備,釋放資源。 其實(shí)pcap的應(yīng)用程序格式很簡(jiǎn)單,總的來(lái)說(shuō)可以可以分為以下5部分,相信看了以下的5部分,大概能對(duì)pcap的總體布局有個(gè)大概的了解了吧: 1.我們從決定用哪一個(gè)接口進(jìn)行嗅探開(kāi)始。在Linux中,這可能是eth0,而在BSD系統(tǒng)中則可能是xl1等等。我們也可以用一個(gè)字符串來(lái)定義這個(gè)設(shè)備,或者采用pcap提供的接口名來(lái)工作。 2.初始化pcap。在這里我們要告訴pcap對(duì)什么設(shè)備進(jìn)行嗅探。假如愿意的話,我們還可以嗅探多個(gè)設(shè)備。怎樣區(qū)分它們呢?使用 文件句柄。就像打開(kāi)一個(gè)文件進(jìn)行讀寫一樣,必須命名我們的嗅探“會(huì)話”,以此使它們各自區(qū)別開(kāi)來(lái)。 3.假如我們只想嗅探特定的傳輸(如TCP/IP包,發(fā)往端口23的包等等),我們必須創(chuàng)建一個(gè)規(guī)則集合,編譯并且使用它。這個(gè)過(guò)程分為三個(gè)相 互緊密關(guān)聯(lián)的階段。規(guī)則集合被置于一個(gè)字符串內(nèi),并且被轉(zhuǎn)換成能被pcap讀的格式(因此編譯它)。編譯實(shí)際上就是在我們的程序里調(diào)用一個(gè)不被外部程序使 用的函數(shù)。接下來(lái)我們要告訴 pcap使用它來(lái)過(guò)濾出我們想要的那一個(gè)會(huì)話。 4.最后,我們告訴pcap進(jìn)入它的主體執(zhí)行循環(huán)。在這個(gè)階段內(nèi)pcap一直工作到它接收了所有我們想要的包為止。每當(dāng)它收到一個(gè)包就調(diào)用另一 個(gè)已經(jīng)定義好的函數(shù),這個(gè)函數(shù)可以做我們想要的任何工作,它可以剖析所部獲的包并給用戶打印出結(jié)果,它可以將結(jié)果保存為一個(gè)文件,或者什么也不作。 5.在嗅探到所需的數(shù)據(jù)后,我們要關(guān)閉會(huì)話并結(jié)束。 三、實(shí)現(xiàn)libpcap的每一個(gè)步驟 (1)設(shè)置設(shè)備 這是很簡(jiǎn)單的。有兩種方法設(shè)置想要嗅探的設(shè)備。 第一種,我們可以簡(jiǎn)單的讓用戶告訴我們??疾煜旅娴某绦?#xff1a; #include #include int main(int argc, char *argv[]) { char *dev = argv[1]; printf("Device: %s", dev); return(0); } 用戶通過(guò)傳遞給程序的第一個(gè)參數(shù)來(lái)指定設(shè)備。字符串“dev”以pcap能“理解”的格式保存了我們要嗅探的接口的名字(當(dāng)然,用戶必須給了我們一個(gè)真正存在的接口)。 另一種也是同樣的簡(jiǎn)單。來(lái)看這段程序: #include #include int main() { char *dev, errbuf[PCAP_ERRBUF_SIZE]; dev = pcap_lookupdev(errbuf); printf("Device: %s", dev); return(0); } (2)打開(kāi)設(shè)備進(jìn)行嗅探 創(chuàng)建一個(gè)嗅探會(huì)話的任務(wù)真的非常簡(jiǎn)單。為此,我們使用pcap_open_live()函數(shù)。此函數(shù)的原型(根據(jù)pcap的手冊(cè)頁(yè))如下: pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) 其第一個(gè)參數(shù)是我們?cè)谏弦还?jié)中指定的設(shè)備,snaplen是整形的,它定義了將被pcap捕捉的最大字節(jié)數(shù)。當(dāng)promisc設(shè)為true時(shí)將 置指定接口為混雜模式(然而,當(dāng)它置為false時(shí)接口仍處于混雜模式的非凡情況也是有可能的)。to_ms是讀取時(shí)的超時(shí)值,單位是毫秒(假如為0則一 直嗅探直到錯(cuò)誤發(fā)生,為-1則不確定)。最后,ebuf是一個(gè)我們可以存入任何錯(cuò)誤信息的字符串(就像上面的errbuf)。此函數(shù)返回其會(huì)話句柄。 混雜模式與非混雜模式的區(qū)別:這兩種方式區(qū)別很大。一般來(lái)說(shuō),非混雜模式的嗅探器中,主機(jī)僅嗅探那些跟它直接有關(guān)的通信,如發(fā)向它的,從它發(fā)出 的,或經(jīng)它路由的等都會(huì)被嗅探器捕捉。而在混雜模式中則嗅探傳輸線路上的所有通信。在非交換式網(wǎng)絡(luò)中,這將是整個(gè)網(wǎng)絡(luò)的通信。這樣做最明顯的優(yōu)點(diǎn)就是使更 多的包被嗅探到,它們因你嗅探網(wǎng)絡(luò)的原因或者對(duì)你有幫助,或者沒(méi)有。但是,混雜模式是可被探測(cè)到的。一個(gè)主機(jī)可以通過(guò)高強(qiáng)度的測(cè)試判定另一臺(tái)主機(jī)是否正在 進(jìn)行混雜模式的嗅探。其次,它僅在非交換式的網(wǎng)絡(luò)環(huán)境中有效工作(如集線器,或者交換中的ARP層面)。再次,在高負(fù)荷的網(wǎng)絡(luò)中,主機(jī)的系統(tǒng)資源將消耗的 非常嚴(yán)重。 (3)過(guò)濾通信 實(shí)現(xiàn)這一過(guò)程由pcap_compile()與pcap_setfilter()這兩個(gè)函數(shù)完成。 在使用我們自己的過(guò)濾器前必須編譯它。過(guò)濾表達(dá)式被保存在一個(gè)字符串中(字符數(shù)組)。其句法在tcpdump的手冊(cè)頁(yè)中被證實(shí)非常好。我建議你親自閱讀它。但是我們將使用簡(jiǎn)單的測(cè)試表達(dá)式,這樣你可能很輕易理解我的例子。 我們調(diào)用pcap_compile()來(lái)編譯它,其原型是這樣定義的: int pcap_compile(pcap_t *p, strUCt bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) 第一個(gè)參數(shù)是會(huì)話句柄。接下來(lái)的是我們存儲(chǔ)被編譯的過(guò)濾器版本的地址的引用。再接下來(lái)的則是表達(dá)式本身,存儲(chǔ)在規(guī)定的字符串格式里。再下邊是一 個(gè)定義表達(dá)式是否被優(yōu)化的整形量(0為false,1為true,標(biāo)準(zhǔn)規(guī)定)。最后,我們必須指定應(yīng)用此過(guò)濾器的網(wǎng)絡(luò)掩碼。函數(shù)返回-1為失敗,其他的任 何值都表明是成功的。 表達(dá)式被編譯之后就可以使用了?,F(xiàn)在進(jìn)入pcap_setfilter()。仿照我們介紹pcap的格式,先來(lái)看一看pcap_setfilter()的原型: int pcap_setfilter(pcap_t *p, struct bpf_program *fp) 這非常直觀,第一個(gè)參數(shù)是會(huì)話句柄,第二個(gè)參數(shù)是被編譯表達(dá)式版本的引用(可推測(cè)出它與pcap_compile()的第二個(gè)參數(shù)相同)。 下面的代碼示例可能能使你更好的理解: #include pcap_t *handle; /* 會(huì)話的句柄 */ char dev[] = "eth0"; /* 執(zhí)行嗅探的設(shè)備 */ char errbuf[PCAP_ERRBUF_SIZE]; /* 存儲(chǔ)錯(cuò)誤 信息的字符串 */ struct bpf_program filter; /*已經(jīng)編譯好的過(guò)濾表達(dá)式*/ char filter_app[] = "port 23"; /* 過(guò)濾表達(dá)式*/ bpf_u_int32 mask; /* 執(zhí)行嗅探的設(shè)備的網(wǎng)絡(luò)掩碼 */ bpf_u_int32 net; /* 執(zhí)行嗅探的設(shè)備的IP地址 */ pcap_lookupnet(dev, &net, &mask, errbuf); handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf); pcap_compile(handle, &filter, filter_app, 0, net); pcap_setfilter(handle, &filter); 這個(gè)程序使嗅探器嗅探經(jīng)由端口23的所有通信,使用混雜模式,設(shè)備是eth0。 (4)實(shí)際的嗅探 有兩種手段捕捉包。我們可以一次只捕捉一個(gè)包,也可以進(jìn)入一個(gè)循環(huán),等捕捉到多個(gè)包再進(jìn)行處理。我們將先看看怎樣去捕捉單個(gè)包,然后再看看使用循環(huán)的方法。為此,我們使用函數(shù)pcap_next()。 pcap_next()的原型及其簡(jiǎn)單: u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) 第一個(gè)參數(shù)是會(huì)話句柄,第二個(gè)參數(shù)是指向一個(gè)包括了當(dāng)前數(shù)據(jù)包總體信息(被捕捉時(shí)的時(shí)間,包的長(zhǎng)度,其被指定的部分長(zhǎng)度)的結(jié)構(gòu)體的指針(在這 里只有一個(gè)片斷,只作為一個(gè)示例)。pcap_next()返回一個(gè)u_char指針給被這個(gè)結(jié)構(gòu)體描述的包。我們將稍后討論這種實(shí)際讀取包本身的手段。 這里有一個(gè)演示怎樣使用pcap_next()來(lái)嗅探一個(gè)包的例子: #include #include int main() { pcap_t *handle; /* 會(huì)話句柄 */ char *dev; /* 執(zhí)行嗅探的設(shè)備 */ char errbuf[PCAP_ERRBUF_SIZE]; /* 存儲(chǔ)錯(cuò)誤信息的字符串 */ struct bpf_program filter; /* 已經(jīng)編譯好的過(guò)濾器 */ char filter_app[] = "port 23"; /* 過(guò)濾表達(dá)式 */ bpf_u_int32 mask; /* 所在網(wǎng)絡(luò)的掩碼 */ bpf_u_int32 net; /* 主機(jī)的IP地址 */ struct pcap_pkthdr header; /* 由pcap.h定義 */ const u_char *packet; /* 實(shí)際的包 */ /* Define the device */ dev = pcap_lookupdev(errbuf); /* 探查設(shè)備屬性 */ pcap_lookupnet(dev, &net, &mask, errbuf); /* 以混雜模式打開(kāi)會(huì)話 */ handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf); /* 編譯并應(yīng)用過(guò)濾器 */ pcap_compile(handle, &filter, filter_app, 0, net); pcap_setfilter(handle, &filter); /* 截獲一個(gè)包 */ packet = pcap_next(handle, &header); /* 打印它的長(zhǎng)度 */ printf("Jacked a packet with length of [%d] ", header.len); /* 關(guān)閉會(huì)話 */ pcap_close(handle); return(0); } 這個(gè)程序嗅探被pcap_lookupdev()返回的設(shè)備并將它置為混雜模式。它發(fā)現(xiàn)第一個(gè)包經(jīng)過(guò)端口23(telnet)并且告訴用戶此包 的大小(以字 節(jié)為單位)。這個(gè)程序又包含了一個(gè)新的調(diào)用pcap_close(),我們將在后面討論(盡管它的名字就足夠證實(shí)它自己的作用)。 實(shí)際上很少有嗅探程序會(huì)真正的使用pcap_next()。通常,它們使用pcap_loop()或者 pcap_dispatch()(它就是用了pcap_loop())。 pcap_loop()的原型如下: int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 第一個(gè)參數(shù)是會(huì)話句柄,接下來(lái)是一個(gè)整型,它告訴pcap_loop()在返回前應(yīng)捕捉多少個(gè)數(shù)據(jù)包(若為負(fù)值則表示應(yīng)該一直工作直至錯(cuò)誤發(fā) 生)。第三個(gè)參數(shù)是回調(diào)函數(shù)的名稱(正像其標(biāo)識(shí)符所指,無(wú)括號(hào))。最后一個(gè)參數(shù)在有些應(yīng)用里有用,但更多時(shí)候則置為NULL。假設(shè)我們有我們自己的想送往 回調(diào)函數(shù)的參數(shù),另外還有pcap_loop()發(fā)送的參數(shù),這就需要用到它。很明顯,必須是一個(gè)u_char類型的指針以確保結(jié)果正確;正像我們稍后見(jiàn) 到的,pcap使用了很有意思的方法以u(píng)_char指針的形勢(shì)傳遞信息。pcap_dispatch()的用法幾乎相同。唯一不同的是它們?nèi)绾翁幚沓瑫r(shí) (還記得在調(diào)用pcap_open_live()時(shí)怎樣設(shè)置超時(shí)嗎?這就是它起作用的地方)。Pcap_loop()忽略超時(shí)而 pcap_dispatch()則不。關(guān)于它們之間區(qū)別的更深入的討論請(qǐng)參見(jiàn)pcap的手冊(cè)頁(yè)。 回調(diào)函數(shù)的原型: void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); 讓我們更細(xì)致的考察它。首先,你會(huì)注重到該函數(shù)返回void類型,這是符合邏輯的,因?yàn)閜cap_loop()不知道如何去處理一個(gè)回調(diào)返回 值。第一個(gè)參數(shù)相應(yīng)于pcap_loop()的最后一個(gè)參數(shù)。每當(dāng)回調(diào)函數(shù)被老婆 調(diào)用時(shí),無(wú)論最后一個(gè)參數(shù)傳給pcap_loop()什么值,這個(gè)值都會(huì)傳給我們回調(diào)函數(shù)的第一個(gè)參數(shù)。第二個(gè)參數(shù)是pcap頭文件定義的,它包括數(shù)據(jù)包 被嗅探的時(shí)間、大小等信息。結(jié)構(gòu)體pcap_pkhdr在pcap.h中定義如下: struct pcap_pkthdr { struct timeval ts; /* 時(shí)間戳 */ bpf_u_int32 caplen; /* 已捕捉部分的長(zhǎng)度 */ bpf_u_int32 len; /* 該包的脫機(jī)長(zhǎng)度 */ }; 這些量都相當(dāng)明了。最后一個(gè)參數(shù)在它們中是最有意思的,也最讓pcap程序新手感到迷惑。這又是一個(gè)u_char指針,它包含了被pcap_loop()嗅探到的所有包。 但是你怎樣使用這個(gè)我們?cè)谠屠锓Q為packet的變量呢?一個(gè)數(shù)據(jù)包包含許多屬性,因此你可以想象它不只是一個(gè)字符串,而實(shí)質(zhì)上是一個(gè)結(jié)構(gòu)體 的集合(比如,一個(gè)TCP/IP包會(huì)有一個(gè)以太網(wǎng)的頭部,一個(gè)IP頭部,一個(gè)TCP頭部,還有此包的有效載荷)。這個(gè)u_char就是這些結(jié)構(gòu)體的串聯(lián)版 本。為了使用它,我們必須作一些有趣的匹配工作。 下面這些是一些數(shù)據(jù)包的結(jié)構(gòu)體: /* 以太網(wǎng)幀頭部 */ struct sniff_ethernet { u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主機(jī)的地址 */ u_char ether_shost[ETHER_ADDR_LEN]; /* 源主機(jī)的地址 */ u_short ether_type; /* IP? ARP? RARP? etc */ }; /* IP數(shù)據(jù)包的頭部 */ struct sniff_ip { #if BYTE_ORDER == LITTLE_ENDIAN u_int ip_hl:4, /* 頭部長(zhǎng)度 */ ip_v:4; /* 版本號(hào) */ #if BYTE_ORDER == BIG_ENDIAN u_int ip_v:4, /* 版本號(hào) */ ip_hl:4; /* 頭部長(zhǎng)度 */ #endif #endif /* not _IP_VHL */ u_char ip_tos; /* 服務(wù)的類型 */ u_short ip_len; /* 總長(zhǎng)度 */ u_short ip_id; /*包標(biāo)志號(hào) */ u_short ip_off; /* 碎片偏移 */ #define IP_RF 0x8000 /* 保留的碎片標(biāo)志 */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* 多碎片標(biāo)志*/ #define IP_OFFMASK 0x1fff /*分段位 */ u_char ip_ttl; /* 數(shù)據(jù)包的生存時(shí)間 */ u_char ip_p; /* 所使用的協(xié)議 */ u_short ip_sum; /* 校驗(yàn)和 */ struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/ }; /* TCP 數(shù)據(jù)包的頭部 */ struct sniff_tcp { u_short th_sport; /* 源端口 */ u_short th_dport; /* 目的端口 */ tcp_seq th_seq; /* 包序號(hào) */ tcp_seq th_ack; /* 確認(rèn)序號(hào) */ #if BYTE_ORDER == LITTLE_ENDIAN u_int th_x2:4, /* 還沒(méi)有用到 */ th_off:4; /* 數(shù)據(jù)偏移 */ #endif #if BYTE_ORDER == BIG_ENDIAN u_int th_off:4, /* 數(shù)據(jù)偏移*/ th_x2:4; /*還沒(méi)有用到 */ #endif u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR) u_short th_win; /* TCP滑動(dòng)窗口 */ u_short th_sum; /* 頭部校驗(yàn)和 */ u_short th_urp; /* 緊急服務(wù)位 */ }; pcap嗅探數(shù)據(jù)包時(shí)正是使用的這些結(jié)構(gòu)。接下來(lái),它簡(jiǎn)單的創(chuàng)建一個(gè)u_char字符串并且將這些結(jié)構(gòu)體填入。那么我們?cè)鯓硬拍軈^(qū)分它們呢?預(yù)備好見(jiàn)證指針最實(shí)用的好處之一吧。 我們?cè)僖淮渭俣ㄒ獙?duì)以太網(wǎng)上的TCP/IP包進(jìn)行處理。同樣的手段可以應(yīng)用于任何數(shù)據(jù)包,唯一的區(qū)別是你實(shí)際所使用的結(jié)構(gòu)體的類型。讓我們從聲明分解u_char包的變量開(kāi)始: const struct sniff_ethernet *ethernet; /* 以太網(wǎng)幀頭部*/ const struct sniff_ip *ip; /* IP包頭部 */ const struct sniff_tcp *tcp; /* TCP包頭部 */ const char *payload; /* 數(shù)據(jù)包的有效載荷*/ /*為了讓它的可讀性好,我們計(jì)算每個(gè)結(jié)構(gòu)體中的變量大小*/ int size_ethernet = sizeof(struct sniff_ethernet); int size_ip = sizeof(struct sniff_ip); int size_tcp = sizeof(struct sniff_tcp); 現(xiàn)在我們開(kāi)始讓人感到有些神秘的匹配: ethernet = (struct sniff_ethernet*)(packet); ip = (struct sniff_ip*)(packet + size_ethernet); tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip); payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp); 此處如何工作?考慮u_char在內(nèi)存中的層次。基本的,當(dāng)pcap將這些結(jié)構(gòu)體填入u_char的時(shí)候是將這些數(shù)據(jù)存入一個(gè)字符串中,那個(gè)字 符串將被送入我們的回調(diào)函數(shù)中。反向轉(zhuǎn)換是這樣的,不考慮這些結(jié)構(gòu)體制中的值,它們的大小將是一致的。例如在我的平臺(tái)上,一個(gè) sniff_ethernet結(jié)構(gòu)體的大小是14字節(jié)。一個(gè)sniff_ip結(jié)構(gòu)體是20字節(jié),一個(gè)sniff_tcp結(jié)構(gòu)體也是20字節(jié)。 u_char指針正是包含了內(nèi)存地址的一個(gè)變量,這也是指針的實(shí)質(zhì),它指向內(nèi)存的一個(gè)區(qū)域。簡(jiǎn)單而言,我們說(shuō)指針指向的地址為x,假如三個(gè)結(jié)構(gòu)體恰好線性 排列,第一個(gè)(sniff_ethernet)被裝載到內(nèi)存地址的x處則我們很輕易的發(fā)現(xiàn)其他結(jié)構(gòu)體的地址,讓我們以表格顯示之: Variable Location (in bytes) sniff_ethernet X sniff_ip X + 14 sniff_tcp X + 14 + 20 payload X + 14 + 20 + 20 結(jié)構(gòu)體sniff_ethernet正好在x處,緊接著它的sniff_ip則位于x加上它本身占用的空間(此例為14字節(jié)),依此類推可得全部地址。 注重:你沒(méi)有假定你的變量也是同樣大小是很重要的。你應(yīng)該總是使用sizeof()來(lái)確保尺寸的正確。這是因?yàn)檫@些結(jié)構(gòu)體中的每個(gè)成員在不同平臺(tái)下可以有不同的尺寸。 下面是主要函數(shù)接口: pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) 獲得用于捕獲網(wǎng)絡(luò)數(shù)據(jù)包的數(shù)據(jù)包捕獲描述字。device參數(shù)為指定打開(kāi) 的網(wǎng)絡(luò)設(shè)備名。snaplen參數(shù)定義捕獲數(shù)據(jù)的最大字節(jié)數(shù)。promisc指定 是否將網(wǎng)絡(luò)接口置于混雜模式。to_ms參數(shù)指定超時(shí)時(shí)間(毫秒)。 ebuf參數(shù)則僅在pcap_open_live()函數(shù)出錯(cuò)返回NULL時(shí)用于傳遞錯(cuò)誤消 息。 pcap_t *pcap_open_offline(char *fname, char *ebuf) 打開(kāi)以前保存捕獲數(shù)據(jù)包的文件,用于讀取。fname參數(shù)指定打開(kāi)的文 件名。該文件中的數(shù)據(jù)格式與tcpdump和tcpslice兼容。"-"為標(biāo)準(zhǔn)輸 入。ebuf參數(shù)則僅在pcap_open_offline()函數(shù)出錯(cuò)返回NULL時(shí)用于傳 遞錯(cuò)誤消息。 pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname) 打開(kāi)用于保存捕獲數(shù)據(jù)包的文件,用于寫入。fname參數(shù)為"-"時(shí)表示 標(biāo)準(zhǔn)輸出。出錯(cuò)時(shí)返回NULL。p參數(shù)為調(diào)用pcap_open_offline()或 pcap_open_live()函數(shù)后返回的pcap結(jié)構(gòu)指針。fname參數(shù)指定打開(kāi) 的文件名。如果返回NULL,則可調(diào)用pcap_geterr()函數(shù)獲取錯(cuò)誤消 息。 char *pcap_lookupdev(char *errbuf) 用于返回可被pcap_open_live()或pcap_lookupnet()函數(shù)調(diào)用的網(wǎng)絡(luò) 設(shè)備名指針。如果函數(shù)出錯(cuò),則返回NULL,同時(shí)errbuf中存放相關(guān)的 錯(cuò)誤消息。 int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) 獲得指定網(wǎng)絡(luò)設(shè)備的網(wǎng)絡(luò)號(hào)和掩碼。netp參數(shù)和maskp參數(shù)都是 bpf_u_int32指針。如果函數(shù)出錯(cuò),則返回-1,同時(shí)errbuf中存放相 關(guān)的錯(cuò)誤消息。 int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 捕獲并處理數(shù)據(jù)包。cnt參數(shù)指定函數(shù)返回前所處理數(shù)據(jù)包的最大值。 cnt=-1表示在一個(gè)緩沖區(qū)中處理所有的數(shù)據(jù)包。cnt=0表示處理所有 數(shù)據(jù)包,直到產(chǎn)生以下錯(cuò)誤之一:讀取到EOF;超時(shí)讀取。callback 參數(shù)指定一個(gè)帶有三個(gè)參數(shù)的回調(diào)函數(shù),這三個(gè)參數(shù)為:一個(gè)從 pcap_dispatch()函數(shù)傳遞過(guò)來(lái)的u_char指針,一個(gè)pcap_pkthdr結(jié)構(gòu) 的指針,和一個(gè)數(shù)據(jù)包大小的u_char指針。如果成功則返回讀取到的 字節(jié)數(shù)。讀取到EOF時(shí)則返回零值。出錯(cuò)時(shí)則返回-1,此時(shí)可調(diào)用 pcap_perror()或pcap_geterr()函數(shù)獲取錯(cuò)誤消息。 int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 功能基本與pcap_dispatch()函數(shù)相同,只不過(guò)此函數(shù)在cnt個(gè)數(shù)據(jù)包 被處理或出現(xiàn)錯(cuò)誤時(shí)才返回,但讀取超時(shí)不會(huì)返回。而如果為 pcap_open_live()函數(shù)指定了一個(gè)非零值的超時(shí)設(shè)置,然后調(diào)用 pcap_dispatch()函數(shù),則當(dāng)超時(shí)發(fā)生時(shí)pcap_dispatch()函數(shù)會(huì)返回。 cnt參數(shù)為負(fù)值時(shí)pcap_loop()函數(shù)將始終循環(huán)運(yùn)行,除非出現(xiàn)錯(cuò)誤。 void pcap_dump(u_char *user, struct pcap_pkthdr *h, u_char *sp) 向調(diào)用pcap_dump_open()函數(shù)打開(kāi)的文件輸出一個(gè)數(shù)據(jù)包。該函數(shù)可 作為pcap_dispatch()函數(shù)的回調(diào)函數(shù)。 int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) 將str參數(shù)指定的字符串編譯到過(guò)濾程序中。fp是一個(gè)bpf_program結(jié) 構(gòu)的指針,在pcap_compile()函數(shù)中被賦值。optimize參數(shù)控制結(jié)果 代碼的優(yōu)化。netmask參數(shù)指定本地網(wǎng)絡(luò)的網(wǎng)絡(luò)掩碼。 int pcap_setfilter(pcap_t *p, struct bpf_program *fp) 指定一個(gè)過(guò)濾程序。fp參數(shù)是bpf_program結(jié)構(gòu)指針,通常取自 pcap_compile()函數(shù)調(diào)用。出錯(cuò)時(shí)返回-1;成功時(shí)返回0。 u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) 返回指向下一個(gè)數(shù)據(jù)包的u_char指針。 int pcap_datalink(pcap_t *p) 返回?cái)?shù)據(jù)鏈路層類型,例如DLT_EN10MB。 int pcap_snapshot(pcap_t *p) 返回pcap_open_live被調(diào)用后的snapshot參數(shù)值。 int pcap_is_swapped(pcap_t *p) 返回當(dāng)前系統(tǒng)主機(jī)字節(jié)與被打開(kāi)文件的字節(jié)順序是否不同。 int pcap_major_version(pcap_t *p) 返回寫入被打開(kāi)文件所使用的pcap函數(shù)的主版本號(hào)。 int pcap_minor_version(pcap_t *p) 返回寫入被打開(kāi)文件所使用的pcap函數(shù)的輔版本號(hào)。 int pcap_stats(pcap_t *p, struct pcap_stat *ps) 向pcap_stat結(jié)構(gòu)賦值。成功時(shí)返回0。這些數(shù)值包括了從開(kāi)始 捕獲數(shù)據(jù)以來(lái)至今共捕獲到的數(shù)據(jù)包統(tǒng)計(jì)。如果出錯(cuò)或不支持 數(shù)據(jù)包統(tǒng)計(jì),則返回-1,且可調(diào)用pcap_perror()或 pcap_geterr()函數(shù)來(lái)獲取錯(cuò)誤消息。 FILE *pcap_file(pcap_t *p) 返回被打開(kāi)文件的文件名。 int pcap_fileno(pcap_t *p) 返回被打開(kāi)文件的文件描述字號(hào)碼。 void pcap_perror(pcap_t *p, char *prefix) 在標(biāo)準(zhǔn)輸出設(shè)備上顯示最后一個(gè)pcap庫(kù)錯(cuò)誤消息。以prefix參 數(shù)指定的字符串為消息頭。 char *pcap_geterr(pcap_t *p) 返回最后一個(gè)pcap庫(kù)錯(cuò)誤消息。 char *pcap_strerror(int error) 如果strerror()函數(shù)不可用,則可調(diào)用pcap_strerror函數(shù)替代。 void pcap_close(pcap_t *p) 關(guān)閉p參數(shù)相應(yīng)的文件,并釋放資源。轉(zhuǎn)載于:https://www.cnblogs.com/newjiang/p/6006826.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的linux-pcap 抓包程序框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 笔记本相机测试软件,联想笔记本人脸识别软
- 下一篇: verilog代码编写工具