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->addrstruct sockaddr *,我們先要轉成struct sockaddr_in *再給地址資料sin_addr然後要給指標當參數使用&,所以最後就是&((struct sockaddr_in *)a->addr)->sin_addr。 其他的a->netmaska->broadaddr以及a->dstaddr做法相同。


    //free
    pcap_freealldevs(devices);

最後記得要釋放記憶體。

結語

這個程式主要是用在Windows上,因為Windows的Interface名稱不容易記憶且會改變,利用這個程式可以用來提示使用者選擇抓取哪個介面的封包。

results matching ""

    No results matching ""