2017年1月20日 星期五

libpcap - Offline filter(14)


Wireshark可以在擷取封包時候做過濾,也可以在抓完後封包顯示特定的封包,前者叫做capture filter,後者叫做display filter;而後者是Wireshark的功能,前者則是libpcap本身的功能,那如果在libpcap中讀取儲存的pcap的想要再使用過濾器的話,libpcap提供了user space的過濾方式,語法一模一樣,只是在user space上做過濾。




首先從命令列讀取過濾器表達式。
    const char *filter = "";
    if(argc == 2) {
        filter = argv[1];
    }//end if

預設過濾器沒有表示不用過濾任何東西,但是當變數argc等於2時候,表示命令列有傳參數進來,所以就修改變數filter指向的位置。


然後開啟之前儲存的saved.pcap檔案。
    char errbuf[PCAP_ERRBUF_SIZE];
    const char *filename = "saved.pcap";

    pcap_t *handle = pcap_open_offline(filename, errbuf);
    if(!handle) {
        fprintf(stderr, "pcap_open_offline(): %s\n", errbuf);
        exit(1);
    }//end if
    printf("Open: %s\n", filename);


接著將過濾器表達式轉成過濾器,使用函數pcap_compile()
    //compile filter
    struct bpf_program fcode;
    if(-1 == pcap_compile(handle, &fcode, filter, 1, PCAP_NETMASK_UNKNOWN)) {
        fprintf(stderr, "pcap_compile(): %s\n", pcap_geterr(handle));
        pcap_close(handle);
        exit(1);
    }//end if

    if(strlen(filter) != 0) {
        printf("Filter: %s\n", filter);
    }//end if

因為是開啟一個檔案,並不知道netmask是什麼,所以用marcos PCAP_NETMASK_UNKNOWN


接著就跟一般抓封包方法一樣讀取封包。
    int total_amount = 0;
    int total_bytes = 0;
    while(1) {
        struct pcap_pkthdr *header = NULL;
        const u_char *content = NULL;
        int ret =
        pcap_next_ex(handle, &header, &content);
        if(ret == 1) {
            if(pcap_offline_filter(&fcode, header, content) != 0) {  
                total_amount++;
                total_bytes += header->caplen;
            }//end if match
        }//end if success
        else if(ret == 0) {
            printf("Timeout\n");
        }//end if timeout
        else if(ret == -1) {
            fprintf(stderr, "pcap_next_ex(): %s\n", pcap_geterr(handle));
        }//end if fail
        else if(ret == -2) {
            printf("No more packet from file\n");
            break;
        }//end if read no more packet
    }//end while


當ret==1時候,表示讀取到封包了,接著使用函數pcap_offline_filter()來過濾,當回傳值不等於0時表示該封包符合過濾條件。
        if(ret == 1) {
            if(pcap_offline_filter(&fcode, header, content) != 0) {  
                total_amount++;
                total_bytes += header->caplen;
            }//end if match
        }//end if success

函數pcap_offline_filter()原型:
int pcap_offline_filter(const struct bpf_program *program, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
  • 回傳值:符合過濾條件傳回非0數值,不符合傳回0。
  • 參數:program過濾器。pkt_header封包的表頭指標。pkt_data封包內容指標。
  • 功能:在user space空間上做封包過濾。

這個函數不是只能用在檔案的情況,因為他的功能就是在user space上做過濾,所以即使封包是從一個device抓上來的也可以過濾,當然速度會比較慢。


編譯:
libpcap % gcc -I/usr/local/opt/libpcap/include -Wall -std=gnu99 -L/usr/local/opt/libpcap/lib -lpcap  offline-filter.c -o offline-filter


執行結果:
libpcap % ./offline-filter 
Open: saved.pcap
No more packet from file
Read: 1000, byte: 232946 bytes

libpcap % ./offline-filter arp
Open: saved.pcap
Filter: arp
No more packet from file
Read: 4, byte: 168 bytes

libpcap % ./offline-filter "tcp or udp"
Open: saved.pcap
Filter: tcp or udp
No more packet from file
Read: 937, byte: 228256 bytes

libpcap % ./offline-filter "tcp and udp"
Open: saved.pcap
pcap_compile(): expression rejects all packets

記得當過濾器表達式有空白時,要用雙引號包起來;最後一個剛好是不符合邏輯的一個條件,因為並不會有任何封包同時為TCP和UDP。

Source code on Github


沒有留言:

張貼留言