LINUX C网络抓包分析

    本文地址:http://www.tongxinmao.com/Article/Detail/id/227

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #include <errno.h>
    #include <unistd.h>
    #include <linux/if_packet.h>
    #include <netinet/if_ether.h>
    #include <netinet/in.h>
    
    //#include <cap.h>
    
    
    
    typedef unsigned char BYTE;
    typedef struct _iphdr //定义IP首部
    {
        unsigned char h_verlen; //4位首部长度+4位IP版本号
        unsigned char tos; //8位服务类型TOS
        unsigned short total_len; //16位总长度(字节)
        unsigned short ident; //16位标识
        unsigned short frag_and_flags; //3位标志位
        unsigned char ttl; //8位生存时间 TTL
        unsigned char proto; //8位协议 (TCP, UDP 或其他)
        unsigned short checksum; //16位IP首部校验和
        unsigned int sourceIP; //32位源IP地址
        unsigned int destIP; //32位目的IP地址
    }IP_HEADER;
    
    typedef struct _udphdr //定义UDP首部
    {
        unsigned short uh_sport;    //16位源端口
        unsigned short uh_dport;    //16位目的端口
        unsigned int uh_len;//16位UDP包长度
        unsigned int uh_sum;//16位校验和
    }UDP_HEADER;
    
    typedef struct _tcphdr //定义TCP首部
    {
        unsigned short th_sport; //16位源端口
        unsigned short th_dport; //16位目的端口
        unsigned int th_seq; //32位序列号
        unsigned int th_ack; //32位确认号
        unsigned char th_lenres;//4位首部长度/6位保留字
        unsigned char th_flag; //6位标志位
        unsigned short th_win; //16位窗口大小
        unsigned short th_sum; //16位校验和
        unsigned short th_urp; //16位紧急数据偏移量
    }TCP_HEADER;
    
    typedef struct _icmphdr {
        unsigned char  icmp_type;
        unsigned char icmp_code; /* type sub code */
        unsigned short icmp_cksum;
        unsigned short icmp_id;
        unsigned short icmp_seq;
        /* This is not the std header, but we reserve space for time */
        unsigned short icmp_timestamp;
    }ICMP_HEADER;
    
    
    /* ip首部长度 */
    #define IP_HEADER_LEN sizeof(IP_HEADER)
    /* tcp首部长度 */
    #define TCP_HEADER_LEN sizeof(TCP_HEADER)
    /* ip首部 + tcp首部长度 */
     #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN
    
    
    void analyseIP(IP_HEADER *ip);
    void analyseTCP(IP_HEADER *ip,TCP_HEADER *tcp);
    void analyseUDP(UDP_HEADER *udp);
    void analyseICMP(ICMP_HEADER *icmp);
    
    #define ETH_NAME    "eth0"
    
    
    int revlen=0;
    int iphlen=0;
    int tcphlen=0;
    
    BYTE buf[1024*1024];
    int byteCount=0;
    
    
     //ifconfig  eth0 promisc 设置好网卡混杂模式则不需要编程实现,否则需要编程实现。
    
     int do_promisc(void) {
        int f, s;
        struct ifreq ifr;
        if ( (f=socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)))<0){
            return -1;
        }
        strcpy(ifr.ifr_name, ETH_NAME);
    
        if ((s = ioctl(f, SIOCGIFFLAGS, &ifr))<0){
          close(f);
          return-1;
        }
    
        if(ifr.ifr_flags & IFF_RUNNING){
            printf("eth link up\n");
        }else{
            printf("eth link down\n");
        }
    
        ifr.ifr_flags |= IFF_PROMISC;
        if ((s = ioctl(f, SIOCSIFFLAGS, &ifr)) < 0){
          return -1;
        }
        printf("Setting interface ::: %s ::: to promisc\n\n", ifr.ifr_name);
        return 0;
    }
    
    int check_nic(void)
    {
        struct ifreq ifr;
        int skfd = socket(AF_INET, SOCK_DGRAM, 0);
    
        strcpy(ifr.ifr_name, ETH_NAME);
        if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
        {
            close(skfd);
            return -1;
        }
        if(ifr.ifr_flags & IFF_RUNNING){
            printf("link up\n");
            close(skfd);
            return 0; // 网卡已插上网线
        }else {
            printf("link down\n");
            close(skfd);
            return -1;
        }
    }
    
    void savefile()
    {
            FILE *f;
            char file[64];
            time_t t;
    
         if(byteCount>5){
                sprintf(file,"tcp_%d.prn",time(&t)); //这里相对于当前工作路径
    
                f=fopen(file,"wb");
                if(f!=NULL)
                {
                fwrite(buf,byteCount,1,f);
                fclose(f);
                printf("save prn file:%s %d byte\n",file,byteCount);
                }else
                {
                    printf("can not create file:%s \n",file);
                }
    
    
           }else{
    
               printf("invial data: %u bytes\n",byteCount);
           }
    
           byteCount=0;
    }
    
    int main(int32_t argc, char **argv)
    {
        int32_t opt = 0;
    
        int sockfd;
         IP_HEADER *ip;
        char buf[1024*1000];
        ssize_t n;
        int sport=-1;
        int dport=-1;
    
         //如optstring="ab:c::d::",命令行为getopt.exe -a -b host -ckeke -d haha
         while ((opt = getopt(argc, argv, "d:s:")) != -1) {
             switch (opt) {
             case 'd':
                 dport=atoi(optarg);
                printf("dport=%s\n",optarg);
               break;
    
            case 's':
                sport=atoi(optarg);
                printf("sport=%s\n",optarg);
                break;
    
    
             default:
                break;;
             }
        }
        do_promisc();
    
    
        /* capture ip datagram without ethernet header */
        if ((sockfd = socket(PF_PACKET,  SOCK_DGRAM, htons(ETH_P_IP)))== -1)
        {
            printf("socket error!\n");
            return 1;
        }
    
    
        while (1)
        {
            n = recv(sockfd, buf, sizeof(buf), 0);
            if (n == -1)
            {
                printf("recv error!\n");
                break;
            }
            else if (n<20)
                continue;
    
    
            //接收数据不包括数据链路帧头
            ip = ( IP_HEADER *)(buf);
            revlen=ntohs(ip->total_len);
    
           //analyseIP(ip);
            size_t iplen =  (ip->h_verlen&0x0f)*4;
            iphlen=iplen;
            TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen);
            if (ip->proto == IPPROTO_TCP)
            {
    
                TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen);
                if(22==ntohs(tcp->th_dport))continue;
    
                if((sport>0 && sport==ntohs(tcp->th_sport)) ||  (dport>0 && dport==ntohs(tcp->th_dport)) )
               {
                    printf("revlen len:%u total len:%u\n",n,ntohs(ip->total_len));
                    analyseTCP(ip,tcp);
    
               }
    
            }
    
           /* else if (ip->proto == IPPROTO_UDP)
            {
                UDP_HEADER *udp = (UDP_HEADER *)(buf + iplen);
                analyseUDP(udp);
            }    /*
            else if (ip->proto == IPPROTO_ICMP)
            {
                ICMP_HEADER *icmp = (ICMP_HEADER *)(buf + iplen);
                analyseICMP(icmp);
            }
            else if (ip->proto == IPPROTO_IGMP)
            {
                printf("IGMP----\n");
            }
            else
            {
                printf("other protocol!\n");
            }
            */
            //printf(".");
        }
        close(sockfd);
        return 0;
    }
    
    void analyseIP(IP_HEADER *ip)
    {
        unsigned char* p = (unsigned char*)&ip->sourceIP;
        unsigned char* p2;
    
        p2 = (unsigned char*)&ip->destIP;
        printf("IPP:\t: %u.%u.%u.%u -->\t: %u.%u.%u.%u\n",p[0],p[1],p[2],p[3], p2[0],p2[1],p2[2],p2[3]);
    
    }
    
    
    
    //https://wenku.baidu.com/view/04b0d780e53a580216fcfeaa.html
    void analyseTCP(IP_HEADER *ip,TCP_HEADER *tcp)
    {
         #define FIN 0x01 //FIN:标记数据是否发送完毕
        #define SYN 0x02 //当SYN=1,ACK=0时,表示这是一个请求建立连接的报文段;当SYN=1,ACK=1时,表示对方同意建立连接
        #define RST 0x04 //说明你与主机的连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接
        #define PSH 0x08 //PSH:告诉对方收到该报文段后是否应该立即把数据推送给上层
        #define ACK 0x10 //TCP规定,连接建立后,ACK必须为1。
        #define URG 0x20
    
        int len;
        BYTE *pData;
    
         unsigned char* p = (unsigned char*)&ip->sourceIP;
        unsigned char* p2;
        short int flag;
         flag=tcp->th_flag;
        p2 = (unsigned char*)&ip->destIP;
        printf("tcp:\t %u.%u.%u.%u:%d -->%u.%u.%u.%u:%d   flag=0x%x[%s %s %s %s %s %s]  SEQ:%u ACKNUM:%u\n",p[0],p[1],p[2],p[3],ntohs(tcp->th_sport),  p2[0],p2[1],p2[2],p2[3],ntohs(tcp->th_dport),flag,(flag & ACK)?"ACK":"",(flag & SYN)?"SYN":"",(flag & FIN)?"FIN":"",(flag & PSH)?"PSH":"",(flag & RST)?"RST":"",(flag & URG)?"URG":"",ntohl(tcp->th_seq),ntohl(tcp->th_ack));
    
    
    
    
    
        if((flag & SYN) && !(flag & ACK))
        {
            printf("request connect\n");
        }
    
        if(  (flag & ACK))
        {
           // printf("ack\n");
        }
    
        if(flag & FIN )
        {
            printf("FINISH & disconnected\n");
            savefile();
        }
    
        if(flag & RST )
        {
            printf("connection reset\n");
            byteCount=0;
        }
    
        if(flag & PSH )
        {
    
    
    
    
        }
    
    
             len =(tcp->th_lenres)>>4;
             len *=4;
             tcphlen=len;
    
             pData = ( (BYTE *)tcp )+len;
    
    
             if(revlen-iphlen-tcphlen>0)
             {
                 int datalen=revlen-iphlen-tcphlen;
                 printf("====get data byte:%d-%d-%d=%d  \n", revlen,iphlen,tcphlen,datalen);
                 if(byteCount+datalen<=sizeof(buf))
                 {
                    memcpy(buf+byteCount,pData,datalen);
                    byteCount+=datalen;
                 }
             }
    
    
    }
    
    void analyseUDP(UDP_HEADER *udp)
    {
    
        printf("UDP  Source port: %u--> Dest port: %u\n", ntohs(udp->uh_sport),ntohs(udp->uh_dport));
    
    }
    
    void analyseICMP(ICMP_HEADER *icmp)
    {
        printf("ICMP -----\n");
        printf("type: %u\n", icmp->icmp_type);
        printf("sub code: %u\n", icmp->icmp_code);
    }


    上一篇:VCB计算String数组元素个数
    下一篇:USB声卡描述符