上一講中知道了如何獲取適配的信息,這一將咱們講寫一個程序蔣每個經過適配器的數據包打印出來。html
打開設備的函數是pcap_open().函數原型是網絡
pcap_t* pcap_open(const char* source,int snaplen,int flags,int read_timeout,struct pcap_rmtauth *auth,char * errbuf);‘函數
pcap_rmatauthoop
{spa
int type.操作系統
char *username;;//Zero-terminated string containing the username that has to be used on the remote machine for authentication指針
char *password;code
}htm
snaplen:snaplen 制定要捕獲數據包中的哪些部分。 在一些操做系統中 (好比 xBSD 和 Win32), 驅動能夠被配置成只捕獲數據包的初始化部分: 這樣能夠減小應用程序間複製數據的量,從而提升捕獲效率。本例中,咱們將值定爲65535,它比咱們能遇到的最大的MTU還要大。所以,咱們確信咱們總能收到完整的數據包。blog
flags: 最最重要的flag是用來指示適配器是否要被設置成混雜模式。 通常狀況下,適配器只接收發給它本身的數據包, 而那些在其餘機器之間通信的數據包,將會被丟棄。 相反,若是適配器是混雜模式,那麼無論這個數據包是否是發給個人,我都會去捕獲。也就是說,我會去捕獲全部的數據包。 這意味着在一個共享媒介(好比總線型以太網),WinPcap能捕獲其餘主機的全部的數據包。 大多數用於數據捕獲的應用程序都會將適配器設置成混雜模式,因此,咱們也會在下面的範例中,使用混雜模式。
PCAP_OPENFLAG_PROMISCUOUS:1,它定義了適配器(網卡)是否進入混雜模式(promiscuous mode)。
PCAP_OPENFLAG_DATATX_UDP:2,它定義了數據傳輸(假如是遠程抓包)是否用UDP協議來處理。
PCAP_OPENFLAG_NOCAPTURE_RPCAP:4,它定義了遠程探測器是否捕獲它本身產生的數據包。
to_ms 指定讀取數據的超時時間,以毫秒計(1s=1000ms)。在適配器上進行讀取操做(好比用 pcap_dispatch() 或 pcap_next_ex()) 都會在 to_ms 毫秒時間內響應,即便在網絡上沒有可用的數據包。 在統計模式下,to_ms 還能夠用來定義統計的時間間隔。 將 to_ms 設置爲0意味着沒有超時,那麼若是沒有數據包到達的話,讀操做將永遠不會返回。 若是設置成-1,則狀況剛好相反,不管有沒有數據包到達,讀操做都會當即返回。
read_timeout:以毫秒爲單位。read timeout被用來設置在遇到一個數據包的時候讀操做沒必要當即返回,而是等待一段時間,讓更多的數據包到來後從OS內核一次讀多個數據包。並不是全部的平臺都支持read timeout;在不支持read timeout的平臺上它將被忽略。
auth:一個指向’struct pcap_rmtauth’的指針,保存當一個用戶登陸到某個遠程機器上時的必要信息。假如不是遠程抓包,該指針被設置爲NULL。
errbuf:一個指向用戶申請的緩衝區的指針,存放當該函數出錯時的錯誤信息。
返回值是一個’pcap_t’指針,它能夠做爲下一步調用(例如pcap_compile()等)的參數,而且指定了一個已經打開的Winpcap會話。在遇到問題的狀況下,它返回NULL而且’errbuf’變量保存了錯誤信息。
函數1:
int pcap_loop( pcap_t* p,
int cnt,
pcap_hander callback,
u_char* user
)
收集一羣數據包。pcap_loop()與pcap_dispatch()相似,可是它會一直保持讀數據包的操做直到cnt包被處理或者發生了錯誤。當有活動的讀超時(read timeout)時它並不返回。然而,對pcap_open_live()指定一個非0的讀超時(read timeout),當發生超時的時候調用pcap_dispatch()來接收並處理到來的全部數據包更好。Cnt指明瞭返回以前要處理數據包的最大數目。若是cnt爲負值,pcap_loop()將一直循環(直到發生錯誤才中止)。若是出錯時返回-1;若是cnt用完時返回0;若是在任何包被處理前調用pcap_breakloop()來停止循環將返回-2。因此,若是程序中使用了pcap_breakloop(),必須準確的來判斷返回值是-1仍是-2,而不能簡單的判斷<0。
函數2:
hypedef void (* pcap_handler)(u_char* user,
const struct pcap_pkthdr* pkt_header,
const u_char* pkt_data)
接收數據包的回調函數原型。當用戶程序使用pcap_dispatch()或者pcap_loop(),數據包以這種回調的方法傳給應用程序。用戶參數是用戶本身定義的包含捕獲會話狀態的參數,它必須跟pcap_dispatch()和pcap_loop()的參數相一致。pkt_hader是與抓包驅動有關的頭。pkt_data指向包裏的數據,包括協議頭。
結構體1:
struct pcap_pkthdr {
struct timeval ts;
bpf_u_int32 caplen;
bpf_u_int32 len;
}
ts:時間戳
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
cpalen:當前分組的長度
len:數據包的長度
/*
* 截獲數據包的試驗。先打印出全部網絡適配器的列表,而後選擇
* 想在哪一個適配器上截獲數據包。而後經過pcap_loop()函數將截獲
* 的數據包傳給回調函數packet_handler()處理。
* 經過該程序初步瞭解了使用winpcap截獲數據包的步驟以及一些在
* 截獲數據包時很是重要的函數和結構體。
*/
1 //打開適配器捕獲數據包 2 #include "pcap.h" 3 4 /* packet handler 函數原型 */ 5 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); 6 7 int main() 8 { 9 pcap_if_t *alldevs; 10 pcap_if_t *d; 11 int inum; 12 int i = 0; 13 pcap_t *adhandle; 14 char errbuf[PCAP_ERRBUF_SIZE]; 15 16 /* 獲取本機設備列表 */ 17 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) 18 { 19 fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf); 20 exit(1); 21 } 22 23 /* 打印列表 */ 24 for (d = alldevs; d; d = d->next) 25 { 26 printf("%d. %s", ++i, d->name); 27 if (d->description) 28 printf(" (%s)\n", d->description); 29 else 30 printf(" (No description available)\n"); 31 } 32 33 if (i == 0) 34 { 35 printf("\nNo interfaces found! Make sure WinPcap is installed.\n"); 36 return -1; 37 } 38 39 printf("Enter the interface number (1-%d):", i); 40 scanf("%d", &inum); 41 42 if (inum < 1 || inum > i) 43 { 44 printf("\nInterface number out of range.\n"); 45 /* 釋放設備列表 */ 46 pcap_freealldevs(alldevs); 47 return -1; 48 } 49 50 /* 跳轉到選中的適配器 */ 51 for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++); 52 53 /* 打開設備 */ 54 if ((adhandle = pcap_open(d->name, // 設備名 55 65536, // 65535保證能捕獲到不一樣數據鏈路層上的每一個數據包的所有內容 56 PCAP_OPENFLAG_PROMISCUOUS, // 混雜模式 57 1000, // 讀取超時時間 58 NULL, // 遠程機器驗證 59 errbuf // 錯誤緩衝池 60 )) == NULL) 61 { 62 fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); 63 /* 釋放設備列表 */ 64 pcap_freealldevs(alldevs); 65 return -1; 66 } 67 68 printf("\nlistening on %s...\n", d->description); 69 70 /* 釋放設備列表 */ 71 pcap_freealldevs(alldevs); 72 73 /* 開始捕獲 */ 74 pcap_loop(adhandle, 0, packet_handler, NULL); 75 76 return 0; 77 } 78 79 80 /* 每次捕獲到數據包時,libpcap都會自動調用這個回調函數 */ 81 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) 82 { 83 struct tm *ltime; 84 char timestr[16]; 85 time_t local_tv_sec; 86 87 /* 將時間戳轉換成可識別的格式 */ 88 local_tv_sec = header->ts.tv_sec; 89 ltime = localtime(&local_tv_sec); 90 strftime(timestr, sizeof timestr, "%H:%M:%S", ltime); 91 92 printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len); 93 94 }
當適配器被打開,捕獲工做就能夠用 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錯誤的數據包,因此,WinPcap無法捕獲到它們。
上面的程序將每個數據包的時間戳和長度從 pcap_pkthdr 的首部解析出來,並打印在屏幕上。
請注意,使用 pcap_loop() 函數可能會遇到障礙,主要由於它直接由數據包捕獲驅動所調用。所以,用戶程序是不能直接控制它的。另外一個實現方法(也是提升可讀性的方法),是使用 pcap_next_ex() 函數。有關這個函數的使用,咱們將在下一講爲您展現。 (不用回調方法捕獲數據包).