libpcap/list_interfaces.c
在抓封包時,因為我們要從最底層封包開始抓,所以必須列出可用的網卡,指令ifconfig
:
~ % ifconfig -l
lo0 gif0 stf0 en0 fw0 en1 en2 p2p0 awdl0 bridge0 en4
目前可用的網卡有這些,但是不同系統上有不同的名稱,Mac OS X是en*
,Linux是eth*
,而Windows是\Device\NPF_{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
,雖然可以透過Socket的方式來列出,但是那是很高段的用法,後期再談,這裡先講如何使用libpcap列出所有可用的Interface。
Source Code
//
// list_interfaces.c
// 功能:列出所有可用的interface以及地址。
// Created by 聲華 陳 on 2015/12/27.
//
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, const char * argv[]) {
pcap_if_t *devices = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
char ntop_buf[256];
//get all devices
if(-1 == pcap_findalldevs(&devices, errbuf)) {
fprintf(stderr, "pcap_findalldevs: %s\n", errbuf);
exit(1);
}//end if
//list all device
for(pcap_if_t *d = devices ; d ; d = d->next) {
printf("Interface: %s\n", d->name);
if(d->description) {
printf("\tDescription: %s\n", d->description);
}//end if
printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK) ? "yes" : "no");
//list all address
for(struct pcap_addr *a = d->addresses ; a ; a = a->next) {
sa_family_t family = a->addr->sa_family;
if(family == AF_INET || family == AF_INET6) {
if(a->addr) {
printf("\t\tAddress: %s\n",
inet_ntop(family, &((struct sockaddr_in *)a->addr)->sin_addr, ntop_buf, sizeof(ntop_buf)));
}//end if
if(a->netmask) {
printf("\t\tNetmask: %s\n",
inet_ntop(family, &((struct sockaddr_in *)a->netmask)->sin_addr, ntop_buf, sizeof(ntop_buf)));
}//end if
if(a->broadaddr) {
printf("\t\tBroadcast Address: %s\n",
inet_ntop(family, &((struct sockaddr_in *)a->broadaddr)->sin_addr, ntop_buf, sizeof(ntop_buf)));
}//end if
if(a->dstaddr) {
printf("\t\tDestination Address: %s\n",
inet_ntop(family, &((struct sockaddr_in *)a->dstaddr)->sin_addr, ntop_buf, sizeof(ntop_buf)));
}//end if
}//end else
printf("\n");
}//end for
}//end for
//free
pcap_freealldevs(devices);
return 0;
}
結果
libpcap % ./list_interfaces
Interface: en0
Loopback: no
Address: 0:0:fe80::6e40:8ff
Netmask: ::ffff:ffff:ffff:ffff:0:0
Address: 192.168.1.100
Netmask: 255.255.255.0
Broadcast Address: 192.168.1.255
...略
城市會列出目前可用的Interface。
分析
//get all devices
if(-1 == pcap_findalldevs(&devices, errbuf)) {
fprintf(stderr, "pcap_findalldevs: %s\n", errbuf);
exit(1);
}//end if
先利用pcap_findalldevs()
來取得目前可用的Interface介面鏈結串列(linked list),取得的鏈結串列儲存在devices
變數裡,錯誤就將錯誤訊息顯示出來並結束程式。
//list all device
for(pcap_if_t *d = devices ; d ; d = d->next) {
printf("Interface: %s\n", d->name);
if(d->description) {
printf("\tDescription: %s\n", d->description);
}//end if
printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK) ? "yes" : "no");
...略
因為他是鏈結串列,所以利用迴圈去一個一個取出來使用。列印出名字後,列印描述(Description),以及判斷是不是loopback。
//list all address
for(struct pcap_addr *a = d->addresses ; a ; a = a->next) {
sa_family_t family = a->addr->sa_family;
if(family == AF_INET || family == AF_INET6) {
if(a->addr) {
printf("\t\tAddress: %s\n",
inet_ntop(family, &((struct sockaddr_in *)a->addr)->sin_addr, ntop_buf, sizeof(ntop_buf)));
}//end if
...略
因為各個介面不一定只有一個地址,所以這裡地址也是一個鏈結串列,一樣使用迴圈去跑。
其中sa_family_t
是地址家族,主要常用的有:
AF_LINK
:data-link層的地址AF_INET
:IPv4的地址AF_INET6
:IPv6的地址
我們只針對AF_INET
以及AF_INET6
解析,利用inet_ntop()
將網路順序的地址轉成字串,其中第二個參數是需要網路順序的地址,a->addr
為struct sockaddr *
,我們先要轉成struct sockaddr_in *
再給地址資料sin_addr
然後要給指標當參數使用&
,所以最後就是&((struct sockaddr_in *)a->addr)->sin_addr
。
其他的a->netmask
、a->broadaddr
以及a->dstaddr
做法相同。
//free
pcap_freealldevs(devices);
最後記得要釋放記憶體。
結語
這個程式主要是用在Windows上,因為Windows的Interface名稱不容易記憶且會改變,利用這個程式可以用來提示使用者選擇抓取哪個介面的封包。