Linux下一個簡單sniffer的實現

      Sniffer(嗅探器)是一種基於被動偵聽原理的網絡分析方式。將網絡接口設置在監聽模式,即可以將網上傳輸的源源不斷的信息截獲。對於網絡監聽的基本原理咱們不在贅述,咱們也不開啓網卡的混雜模式,由於如今的網絡基本都使用了交換機,所以混雜模式也彷佛變得無用武之地了。linux

     Sniffer實現的底層支持有多種,如libpcap、套接字方式,libpcap是unix/linux平臺下的網絡數據包捕獲函數包,對應windows下的是winpcap,大多數網絡監控軟件都以它爲基礎,好比大名鼎鼎的wireshark。libpcap目前支持源自Berkeley內核中的BPF、Solaris 2.X 和HP-UX中的DLPI、SunOS 4.1.x中的NIT、Linux的SOCK_PACKET套接字和PF_PACKET套接字。也就是說,linpcap在linux下的實現是基於以上兩類套接字的,而咱們主要討論的就是直接經過套接字來獲取鏈路層的數據。windows

      Linux前後有兩個從數據鏈路層獲取分組的方法,較舊的方法是建立類型爲SOCK_PACKET的套接字,這個方法的可用面較寬,可是缺少靈活性,較新的方法是建立協議族爲PF_PACKET的套接字,這個方法引入了更多的過濾和性能特性。舉例來講,從數據鏈路層接受全部幀應以下建立套接字:網絡

    fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));       /*較新的方法*/
或
    fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));       /*較舊的方法*/

       這裏要說明的是,這兩個函數的最後一個參數代表了接受鏈路層全部類型的以太網幀,若只想捕獲IPv4幀,那就把最後一個參數改成htons(ETH_P_IP),其宏定義在linux/if_ether.h頭文件中。另外,PF_PACKET協議簇支持兩個不一樣的SOCKET類型,SOCK_DGRAM和SOCK_RAW,用SOCK_RAW建立的套接字,咱們得到的是包含二層頭的幀,而使用SOCK_DGRAM套接字類型,咱們獲取的數據不包含二層的頭。socket

       若是想設置網卡的混雜模式, 對於PF_PACKET套接字,經過設置PACKET_ADD_MEMBERSHIP套接字選項實現,在做爲setsockopt第四個參數傳遞的packet_mreq結構中需指定網絡接口和值爲PACKET_MR_PROMISC。linux的數據鏈路層訪問方法不提供內核緩衝,並且也只有較新的方法提供內核過濾(經過設置SO_ATTACH_FILTER套接字選項安裝),在本程序中咱們沒有使用過濾功能。tcp

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/ip_icmp.h>
#include<netinet/tcp.h>
#include<netinet/udp.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<linux/if_ether.h>
#include<arpa/inet.h>
#include <unistd.h>

#define BUFFSIZE 1024

int main(int argc, char *argv[]){

    int rawsock;
    unsigned char buff[BUFFSIZE];
    int n;
    int count = 0;
    char ch;
    char proto[6],
         saddr[20] = {},
         daddr[20] ={},
         address[20]= {};
    int slen = 0;

    rawsock = socket(PF_PACKET,SOCK_DGRAM, htons(ETH_P_IP));
    if(rawsock < 0){
        printf("raw socket error!\n");
        exit(1);
    }
    while((ch = getopt(argc, argv, "p:s:d:h")) != -1){
        switch (ch) {
            case 'p':
                slen = strlen(optarg);
                if(slen > 4){
                    fprintf(stdout, "The protocol is error!\n");
                    return -1;
                }
                memcpy(proto, optarg, slen);
                proto[slen] = '\0';
                break;
            case 's':
                slen = strlen(optarg);
                if(slen > 15 || slen < 7){
                    fprintf(stdout, "The IP address is error!\n");
                    return -1;
                }
                memcpy(saddr, optarg, slen);
                saddr[slen] = '\0';
                break;
            case 'd':
                slen = strlen(optarg);
                if(slen > 15 || slen < 7){
                    fprintf(stdout, "The IP address is error!\n");
                    return -1;
                }
                memcpy(daddr, optarg, slen);
                saddr[slen] = '\0';
                break;
            case 'h':
                fprintf(stdout, "usage: snffer [-p protocol] [-s source_ip_address] [-d dest_ip_address]\n"
                                "    -p    protocol[TCP/UDP/ICMP]\n"
                                "    -s    souce ip address\n"
                                "    -d    dest ip address\n");
                exit(0);
            case '?':
                fprintf(stdout, "unrecongized option: %c\n", ch);
                exit(-1);
        }
    }
    while(1){    
        n = recvfrom(rawsock,buff,BUFFSIZE,0,NULL,NULL);
        if(n<0){
            printf("receive error!\n");
            exit(1);
        }

        count++;
        struct ip *ip = (struct ip*)(buff);
        if(strlen(proto)){
            if(!strcmp(proto, "TCP")){
                if(ip->ip_p != IPPROTO_TCP)
                    continue;
                else 
                    goto addr;
            }else if(!strcmp(proto, "UDP")){
                if(ip->ip_p != IPPROTO_UDP)
                    continue;
                else 
                    goto addr;
            }else if(!strcmp(proto, "ICMP")){
                if(ip->ip_p != IPPROTO_ICMP)
                    continue;
                else
                    goto addr;
            }
        }

addr:
        if(strlen(saddr)){
            strcpy(address, inet_ntoa(ip->ip_src));
            if(strcmp(address, saddr) != 0)
                continue;
        }
        if(strlen(daddr)){
            strcpy(address, inet_ntoa(ip->ip_dst));
            if(strcmp(address, daddr) != 0)
                continue;
        }


        printf("%4d    %15s",count,inet_ntoa(ip->ip_src));
        printf("%15s    %5d    %5d\n",inet_ntoa(ip->ip_dst),ip->ip_p,ntohs(ip->ip_len));

        int i=0,j=0;
        for(i=0;i<n;i++){
            if(i!=0 && i%16==0){
                printf("    ");
                for(j=i-16;j<i;j++){
                    if(buff[j]>=32&&buff[j]<=128)
                        printf("%c",buff[j]);
                    else printf(".");
                }
                printf("\n");
            }
            if(i%16 == 0) printf("%04x    ",i);            
            printf("%02x",buff[i]);

            if(i==n-1){
                for(j=0;j<15-i%16;j++) printf("  ");
                printf("    ");
                for(j=i-i%16;j<=i;j++){
                    if(buff[j]>=32&&buff[j]<127)
                        printf("%c",buff[j]);
                    else printf(".");

                }

            }

        }

        printf("\n\n");

    }
}    
相關文章
相關標籤/搜索