【转】libpcap实现机制及接口函数
轉自:libpcap實現機制及接口函數 - 簡書
1.Libpcap 的工作原理
Libpcap的工作原理可以描述為,當一個數據包到達網卡時,通過網絡分接口(即旁路機制)將數據包發給BPF過濾器,匹配通過的數據包可以被libpcap利用創建的套接字PF_PACKET從鏈路層驅動程序中獲得。進而在用戶空間提供獨立于系統的用戶級API接口。
流程圖示意圖
?
一個數據包的捕捉分為三個主要部分
- 面向底層包捕獲、
- 面向中間層的數據包過濾
- 面向應用層的用戶接口
這與Linux操作系統對數據包的處理流程是相同的
網卡->網卡驅動->數據鏈路層->IP層->傳輸層->應用程序
2、Libpcap的實現機制
這里實現的包捕獲機制是在數據鏈路層增加一個旁路處理,并不干擾系統自身的網路協議棧的處理,對發送和接收的數據包通過Linux內核做過濾和緩沖處理,最后直接傳遞給上層應用程序。因此libpcap在捕獲到達網卡的數據包后繞開了傳統linux協議棧處理,直接使用鏈路層PF_PACKET協議族原始套接字方式向用戶空間傳遞報文。
?libpcap捕獲報文機制示意圖
接下來,對照上圖分層解釋報文從網卡最終到達用戶空間的處理流程:
- 網絡報文的接收源自網絡設備(網卡)。
---------------------------物理層
- 網絡設備在接收到一個報文之后,通過中斷IRQ告知CPU。網卡驅動程序需要注冊對該中斷事件的處理函數,以處理接收到的報文。在中斷中執行以下操作:
- 分配一個緩沖區sk_buff,把接收的數據拷貝進去;(第一次拷貝)
- 對緩沖區結構內的一些參數做初始化以告知較高層協議數據是什么類型skb->protocol;
- 非NAPI:調用netif_rx( )函數通知內核,將幀放入CPU的softnet_data->input_pkt_queue。netif_rx會調用網絡接口函數netif_rx_schedule(使用softdate_net結構中內嵌的backlog_dev作為dev參數)
- NAPI:幀存放在每個設備自己的隊列之中。調用netif_rx_schedule函數(直接以對應設備的dev結構為參數)
- 然后觸發相關聯的軟IRQ--NET_RX_SOFTIRQ,此時網卡驅動程序已經將輸入設備排入輪詢列表poll_list,接下來執行net_rx_action函數:
- 瀏覽poll_list設備列表,這些設備的入口隊列都有數據;
- 非NAPI設備:執行process_backlog函數(backlog_dev->poll)
若時間片用完或者配額用盡,將該設備放置列表尾部等待下一次中斷到來時繼續被調用;若處理完input_pkt_queue列表中的全部報文,則將設備退出poll_list同時打開設備中斷服務繼續監聽下一個報文到來。
- NAPI設備:執行poll函數
dev->poll可以做一些輪詢的工作,如果網絡設備已經接收了多個報文,可以一次性處理。就算設備此刻所接收到的報文都已經處理完了,驅動程序也可以根據某種方式預判設備在很短的一段時間內還將收到報文,于是依然將自己對應的dev結構留在poll_list中,處于輪詢狀態。增大了報文接收的平均延時,但避免了大量中斷帶來的開銷。dev設備退出poll_list同時打開設備中斷服務。
- 接著調用netif_receive_skb函數:
- 如果有抓包程序,由網絡分接口進入BPF過濾器,將規則匹配的報文拷貝到系統內核緩存 (第二次拷貝)否則直接丟棄數據包;*注 : linux 在 PF_PACKET 類型的 socket 上支持內核過濾。Linux 內核允許我們把一個名為 LPF(Linux Packet Filter) 的過濾器直接放到 PF_PACKET 類型 socket 的處理過程中,過濾器在網卡接收中斷執行后立即執行 *
- 處理數據鏈路層的橋接功能;
- 根據skb->protocol字段確定上層協議并提交給網絡層處理-->進入網絡協議棧
|
|
| 內核空間
|
/
---------------------------數據鏈路層
/
|
| 用戶空間
|
|
- libpcap繞過了Linux內核收包流程中協議棧部分的處理,使得用戶空間API可以直接調用套接字PF_PACKET從鏈路層驅動程序中獲得數據報文的拷貝,將其從內核緩沖區拷貝至用戶空間緩沖區(第三次拷貝)
fd=socket(PF_PACKET,sock_RAW,htons(ETH_P_ALL))
libpcap 函數庫注冊的報文接收類型為 ETH_P_ALL,即接收所有的網絡數據幀,其處理函數為 packet_rcv()。該函數工作在數據鏈路層。
進而調用recvfrom函數獲得捕獲的報文 (需要進行系統調用):
packet_rcv() 函數將直接調用 skb_queue_tail() 將數據報文存放在代表相應網絡連接控制結構(struct sock)的接收隊列 receive_queue 中。這樣數據報文在接收過程中就繞過了 TCP 層和 IP 層繁瑣的協議處理過程。最后,睡眠在 sk 等待隊列上的函數 packet_recvmsg() 會接收鏈路層數據幀并將該數據幀直接拷貝到應用程序緩沖區中。
- 最后libpcap面向用戶空間提供獨立于系統的可調用的函數接口
3、BPF過濾器
-
BPF本質上來說是一也個設備驅動(device driver),能夠被應用程序用來讀取網絡上通過這個網絡適配器的包。但是BPF又是一個特殊的驅動,因為它并沒有直接控制網絡適配器,而是網絡適配器真正的設備驅動調用BPF來傳遞數據。
-
BPF正常情況下被用作診斷工具去檢查與本機相連的網絡的流通狀況。一個BPF設備能夠配置一個filter,根據這個filter的特征,來忽略或者接收到來的包。
-
BPF擁有兩個組件: the network tap 和 the packet filter 。the network tap 收集來自網絡設備驅動的包的一個拷貝,并把它專遞給監聽程序。the packet filter 決定是否接收這個包并且把它拷貝給監聽程序
-
BPF為每一個要求服務的抓包程序關聯一個filter和兩個buffer。BPF分配buffer 且通常情況下它的額度是4KB the store buffer 被使用來接收來自適配器的數據; the hold buffer被使用來拷貝包到應用程序
-
通常情況下, 當一個包到達網絡接口時, 數據鏈路設備驅動將把它發送到系統協議棧。但是當BPF在這個接口上面監聽時,網絡設備驅動將首先調用 BPF的network tap函數。這個tap函數將包送入每一個監聽程序的filter。而用戶定義的filter決定: 是否接收這個包; 每一個包有多少字節將會被保存。如果filter接收這個包, 那么tap 將會從數據鏈路層驅動的緩存中拷貝這個數目的字節數到 與這個filter關聯的store buffer中(store buffer在內核中定義)。同時,網絡接口的設備驅動將會重新獲得控制權,且正常的協議處理將會進行。
-
監聽進程執行read系統調用去從BPF(hold buffer)接收包,并將阻塞于此。當hold buffer 滿的時候(或者當超時發生時),BPF將會拷貝這些數據到進程內存空間,且喚醒這個進程。監聽程序能夠一次接收多個包。
BPF結構圖
4、關鍵函數
- 未使用NAPI的網絡設備驅動程序通過netif_rx通知內核幀已經接收
5、用戶級API
1)獲取數據包捕獲描述字
函數名稱:pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
函數功能:獲得用于捕獲網絡數據包的數據包捕獲描述字。
參數說明:device參數為指定打開的網絡設備名。snaplen參數定義捕獲數據的最大字節數。Promisc 指定是否將網絡接口置于混雜模式。to_ms參數指*定超時時間(毫秒)。ebuf參數則僅在pcap_open_live()函數出錯返回NULL時用于傳遞錯誤消息。
2)打開保存捕獲數據包文件
函數名稱:pcap_t *pcap_open_offline(char *fname, char *ebuf)
函數功能:打開以前保存捕獲數據包的文件,用于讀取。
參數說明:fname參數指定打開的文件名。該文件中的數據格式與tcpdump和tcpslice兼容。”-“為標準輸入。ebuf參數則僅在pcap_open_offline()函數出錯返回NULL時用于傳遞錯誤消息。
3)轉儲數據包
函數名稱:pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)
函數功能:打開用于保存捕獲數據包的文件,用于寫入。
參數說明:fname參數為”-“時表示標準輸出。出錯時返回NULL。p參數為調用pcap_open_offline() 或pcap_open_live()函數后返回的pcap結構指針,即網卡句柄。fname參數指定打開的文件名,存盤的文件名。如果返回NULL,則可調用pcap_geterr()函數獲取錯誤消息。
4)查找網絡設備
函數名稱:char *pcap_lookupdev(char *errbuf)
函數功能:用于返回可被pcap_open_live()或pcap_lookupnet()函數調用的網絡設備名指針。
返回值:如果函數出錯,則返回NULL,同時errbuf中存放相關的錯誤消息。
5)獲取網絡號和掩碼
函數名稱:int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)
函數功能:獲得指定網絡設備的網絡號和掩碼。
參數說明:netp參數和maskp參數都是bpf_u_int32指針。
返回值:如果函數出錯,則返回-1,同時errbuf中存放相關的錯誤消息。
6)捕獲并處理數據包
** 函數名稱**:int pcap_dispatch(pcap_t *p, int cnt,pcap_handler callback, u_char *user)
函數功能:捕獲并處理數據包。
參數說明:cnt參數指定函數返回前所處理數據包的最大值。cnt= -1表示在一個緩沖區中處理所有的數據包。cnt=0表示處理所有數據包,直到產生以下錯誤之一:讀取到EOF;超時讀取。callback參數指定一個帶有三個參數的回調函數,這三個參數為:一個從pcap_dispatch()函數傳遞過來的u_char指針,一個pcap_pkthdr結構的指針,和一個數據包大小的u_char指針。
返回值:如果成功則返回讀取到的字節數。讀取到EOF時則返回零值。出錯時則返回-1,此時可調用pcap_perror()或pcap_geterr()函數獲取錯誤消息。
** 7)捕獲和處理數據包**
** 函數名稱:int pcap_loop(pcap_t *p, int cnt,pcap_handler callback, u_char *user)
** 函數功能:功能基本與pcap_dispatch()函數相同,只不過此函數在cnt個數據包被處理或出現錯誤時才返回,但讀取超時不會返回。而如果為pcap_open_live()函數指定了一個非零值的超時設置,然后調用pcap_dispatch()函數,則當超時發生時pcap_dispatch()函數會返回。cnt參數為負值時pcap_loop()函數將始終循環運行,除非出現錯誤。
** 8)輸出數據包**
函數名稱:void pcap_dump(u_char *user, struct pcap_pkthdr *h,u_char *sp)
** 函數功能**:向調用pcap_dump_open()函數打開的文件輸出一個數據包。該函數可作為pcap_dispatch()函數的回調函數。
參數說明: 參數1: 所建立的文件pcap_dump_open()的返回值,要進行強制轉換.;參數2: 數據包特有的內容.;參數 3: 數據包內容指針
9)編譯字串至過濾程序
函數名稱:int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask)
函數功能:將str參數指定的字符串編譯到過濾程序中。
參數說明:fp是一個bpf_program結構的指針,在pcap_compile()函數中被賦值。optimize參數控制結果代碼的優化。netmask參數指定本地網絡的網絡掩碼。
10)指定過濾程序
函數名稱:int pcap_setfilter(pcap_t p, struct bpf_program fp)
函數功能:指定一個過濾程序。
參數說明:fp參數是bpf_program結構指針,通常取自pcap_compile()函數調用。
** 返回值:出錯時返回-1;成功時返回0
11)獲取下一個數據包
函數名稱:u_char pcap_next(pcap_t p, struct pcap_pkthdr *h)
** 函數功能:返回指向下一個數據包的u_char指針
12)獲取數據鏈路層類型
函數名稱:int pcap_datalink(pcap_t *p)
** 函數功能**:返回數據鏈路層類型,例如DLT_EN10MB
13)獲取快照參數值
函數名稱:int pcap_snapshot(pcap_t *p)
** 函數功能**:返回pcap_open_live被調用后的snapshot參數值
14)檢測字節順序
函數名稱:int pcap_is_swapped(pcap_t *p)
函數功能:返回當前系統主機字節與被打開文件的字節順序是否不同
15)獲取主版本號
** 函數名稱**:int pcap_major_version(pcap_t *p)
函數功能:返回寫入被打開文件所使用的pcap函數的主版本號
16)獲取輔版本號
函數名稱:int pcap_minor_version(pcap_t *p)
** 函數功能**:返回寫入被打開文件所使用的pcap函數的輔版本號
17)結構賦值
函數名稱:int pcap_stats(pcap_t *p, struct pcap_stat *ps)
函數功能:向pcap_stat結構賦值。成功時返回0。這些數值包括了從開始捕獲數據以來至今共捕獲到的數據包統計。如果出錯或不支持數據包統計,則返回-1,且可調用pcap_perror()或pcap_geterr()函數來獲取錯誤消息。
18)獲取打開文件名
函數名稱:FILE *pcap_file(pcap_t *p)
函數功能:返回被打開文件的文件名。
19)獲取描述字號碼
函數名稱:int pcap_fileno(pcap_t *p)
函數功能:返回被打開文件的文件描述字號碼
** 20)顯示錯誤消息**
函數名稱:void pcap_perror(pcap_t *p, char *prefix)
函數功能:在標準輸出設備上顯示最后一個pcap庫錯誤消息。以prefix參數指定的字符串為消息頭。
6、參考文檔
- 基于 linux 平臺的 libpcap 源代碼分析
- libpcap使用方法
- libpcap編程小結
- tcpdump sniffex
- PF_PACKET
- 原始套接字 SOCK_RAW
- udp數據報從網卡驅動到用戶空間流程總結
- PACKET_MMAP實現原理分析?
- linux網絡報文接收發送淺析?
- 高速網絡環境下基于零拷貝的報文捕獲機制
- 基于Linux平臺的libpcap源碼分析和優化
14人點贊
日記本
作者:shaarawy18
鏈接:https://www.jianshu.com/p/ed6db49a3428
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的【转】libpcap实现机制及接口函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二级资本债券是什么意思?有这些作用!
- 下一篇: 信用卡怎么还款?信用卡还款常见问题及分析