基本原理
1.先創建socket,內核dev_add_packet()掛上自己的鉤子函數
2.然後在鉤子函數packet_recvmsg中,把skb放到自己的接收隊列中,
3.接着系統調用recv取出skb來,把數據包skb->data拷貝到用戶空間
4.最後關閉socket,內核dev_remove_packet()刪除自己的鉤子函數
內核數據處理流程
內核在收到網卡發出的軟中斷後進行數據包的處理,在數據包的處理函數netif_receive_skb中,先檢查ptype_all中是否有註冊的協議,如果有,則調用相應的處理函數,然後再到ptype_base中,找到合適的協議,將skb發送到相關協議的處理函數.比如ip協議(ip_rcv)或者arp(arp_rcv)
數據包的傳遞過程
簡單來說數據包首先到網卡,然後通過軟中斷的形式觸發內核對其處理,放入內存,最後再被應用程序讀取。tcpdump是通過調用libpcap的api函數來實現的。下圖中的BPF(伯克利包過濾,BPF能夠通過比較第2、3、4層協議中各個數據字段值的方法對流量進行過濾)是包過濾器,bufferQ是給應用程序讀取數據包的緩衝隊列,tcpdump就是從bufferQ中抓取數據包。
libpcap在內核收發包的接口處收取skb_clone拷貝的網絡包。skb_clone是內核中用來拷貝skb結構的函數,它會在內存中申請一塊新的塊用來存放拷貝的skb。
libpcap
tcpdump.c正式使用libpcap裏的函數完成兩個最關鍵的動作:獲取捕獲報文的接口,和捕獲報文並將報文交給callback。
相關使用邏輯如下
我們知道tcpdump裏的絕大多數動作是通過調用libpcap接口來實現的,通過這些接口我們自己也可以實現一個簡單的抓包工具。下面是一些主要函數的解析:
pcap_start/pcap_activate:當tcpdump開始進行抓包的時候,會通過pcap_start創建一個句柄,再通過pcap_activate激活句柄
pcap_findalldevs:tcpdump指定對一個網卡列表進行抓包時會調用這個函數
pcap_lookupdev:返回第一個不是迴環的網卡
pcap_open_offline:打開一個包存的文件
pcap_next:接收一個包
pcap_open_live:打開選擇的設備
尋找一個設備
dev = pcap_lookupdev (errbuf); if (dev == NULL) { fprintf (stderr, "Couldn't find default device: %s\n", errbuf); return (2); }
設置過濾器
過濾器將網絡包中過濾出我們想要的內容,通過如下函數創建
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
第一個參數就是pcap_open_live返回的值,fp 是過濾器的版本,optimize表示是否需要優化,最後netmask是過濾器使用的所在子網掩碼。創建完成之後使用pcap_setfilter函數進行編譯,需要編譯後才能使用。
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
抓包動作
有兩種抓包技術:
一次抓一個,第一個參數是創建會話句柄,第二個參數存放包的信息
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
收到包後先攢着,然後一次抓多個(主流)。第一個參數是會話句柄,第二個參數是指定抓多少包,第三個參數是每次抓多少包(每抓完一次這個數量就會進行回調,直到抓滿第二個參數指定的數量),第四個參數表示抓包的用戶(一些用戶會用到)
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
常用例子
tcpdump –iany –Xs0 tcp and host 192.168.1.1 and port 9527
參考資料
http://www.tcpdump.org/manpages/pcap.3pcap.html
https://blog.csdn.net/notbaron/article/details/79735414
https://www.kernel.org/doc/htmldocs/networking/API-skb-clone.html