zoukankan      html  css  js  c++  java
  • UNP1:linux下实现ping程序

    UNP1里面给出了一个ping程序的实现,里面包含了ipv4和ipv6两个版本。

    经过学习,对里面的代码做了一点点小得修改(还原了基本的API),再加了一点注释,测试可以通过。

    经过手敲了这段代码,收获还是很大的。对raw socket的编程有了基本的概念,同时也对icmp包和ip包有了更深入的了解。

    修改后的代码如下,总共分为三个文件:

    ping.h

    #include<stdio.h>
    #include<time.h>
    #include<errno.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<netdb.h>
    #include<string.h>
    #include<strings.h>
    #include<netinet/in.h>
    #include<pthread.h>
    #include<arpa/inet.h>
    #include<sys/socket.h>
    #include<netinet/in_systm.h>
    #include<netinet/ip.h>
    #include<netinet/ip_icmp.h>
    #include<signal.h>
    #define BUFSIZE 1500
    
    char sendbuf[BUFSIZE];
    
    int datalen;
    char *host;
    int nsent;
    pid_t pid;
    int sockfd;
    int verbose;
    
    void proc_v4(char *,ssize_t,struct msghdr*,struct timeval *); 
    void send_v4(void);
    void readloop(void);
    void sig_alarm(int);
    void tv_sub(struct timeval *,struct timeval *); 
    
    struct proto{
        void (*fproc)(char *,ssize_t,struct msghdr *,struct timeval *); 
        void (*fsend)(void);
        void (*finit)(void);
        struct sockaddr *sasend;
        struct sockaddr *sarecv;
        socklen_t salen;
        int icmpproto;
    }*pr;
    

    main.c:获取目标ip

    #include"ping.h"
    struct proto proto_v4={proc_v4,send_v4,NULL,NULL,NULL,0,IPPROTO_ICMP};
    
    int datalen=56;
    struct addrinfo *host_serv(const char *host,const char *serv,int family,int socktype)
    {
        int n;
        struct addrinfo hints,*res;
        
        bzero(&hints,sizeof(hints));
        hints.ai_flags=AI_CANONNAME;
        hints.ai_family=family;
        hints.ai_socktype=socktype;
        if((n=getaddrinfo(host,serv,&hints,&res))!=0){
            return NULL;    
        }   
        return (res);
    }
    
    int main(int argc,char *argv[])
    {
        int c;  
        struct addrinfo *ai;
        char h[20]={0};
        opterr=0;
        while((c=getopt(argc,argv,"v"))!=-1){
            switch(c){
                case 'v':
                    verbose++;
                    break;  
                case '?':
                    printf("unrecognized option: %c\n",c);
                    return 0;
            }   
        
        }   
        if(optind!=argc-1)
            printf("usage: ping [-v] <hostname>\n");
        host=argv[optind];
        pid=getpid() & 0xffff;
        signal(SIGALRM,sig_alarm);
        ai=host_serv(host,NULL,0,0);
        inet_ntop(AF_INET,&((struct sockaddr_in*)(ai->ai_addr))->sin_addr,h,sizeof(h));
        printf("PING %s (%s):%d data bytes\n",
                ai->ai_canonname?ai->ai_canonname:h,h,datalen);
    
        if(ai->ai_family==AF_INET){
            pr=&proto_v4;
        }else{
            printf("unknown address family %d\n",ai->ai_family);
        }
    
        pr->sasend=ai->ai_addr;
        pr->sarecv=(struct sockaddr*)calloc(1,ai->ai_addrlen);
        pr->salen=ai->ai_addrlen;
        readloop();
        exit(0);
    }
    

    readloop.c:发送和接收icmp包

    #include"ping.h"
    
    //get rtt
    void tv_sub(
            struct timeval *out,    //time,tv_sev is microseconds
            struct timeval *in)
    {
        if((out->tv_usec-=in->tv_usec)<0){
            --out->tv_sec;
            out->tv_sec+=1000000;
        }
        out->tv_sec-=in->tv_sec;
    }
    
    void proc_v4(char *ptr,ssize_t len,struct msghdr *msg,struct timeval * tvrecv)
    {
        int hlen1,icmplen;
        char host[20];
        double rtt;
        struct ip *ip;
        struct icmp *icmp;
        struct timeval *tvsend;
    
        ip=(struct ip*)ptr;
        hlen1=ip->ip_hl<<2;//get ipdatagram length,include option
        if(ip->ip_p!=IPPROTO_ICMP){
            return;
        }
        icmp=(struct icmp*)(ptr+hlen1);
        if((icmplen=len-hlen1)<8)
            return;
        if(icmp->icmp_type==ICMP_ECHOREPLY){
            if(icmp->icmp_id!=pid)
                return;
            if(icmplen<16)
                return;
            tvsend=(struct timeval*)icmp->icmp_data;
            tv_sub(tvrecv,tvsend);
            rtt=tvrecv->tv_sec*1000.0+tvrecv->tv_usec/1000.0;
            printf("%d bytes from %s:seq=%u,ttl=%d,rtt=%.3f ms\n",icmplen,inet_ntop(AF_INET,&((struct sockaddr_in*)(pr->sarecv))->sin_addr,host,sizeof(host)),icmp->icmp_seq,ip->ip_ttl,rtt);
        }
    }
    
    //call send data func
    void sig_alarm(int signo)
    {
        (*pr->fsend)();
        alarm(1);
        return;
    }
    
    //create checksum
    uint16_t in_cksum(uint16_t *addr,int len)
    {
        int nleft=len;
        uint32_t sum=0;
        uint16_t *w=addr;
        uint16_t answer=0;
    
        while(nleft>1){
            sum+=*w++;
            nleft-=2;
        }
        if(nleft==1){
            *(unsigned char *)(&answer)=*(unsigned char *)w;
            sum+=answer;
        }
        sum=(sum >> 16)+(sum & 0xffff);
        sum+=(sum>>16);
        answer=~sum;
        return answer;
    }
    
    //send icmp data
    void send_v4(void){
        int len;
        struct icmp *icmp;
    
        //init icmp datagram
        icmp=(struct icmp*)sendbuf;
        icmp->icmp_type=ICMP_ECHO;  //type
        icmp->icmp_code=0;          //code
        icmp->icmp_id=pid;
        icmp->icmp_seq=nsent++;
        gettimeofday((struct timeval *)icmp->icmp_data,NULL);//get send time
        len=8+datalen;
        icmp->icmp_cksum=0;
        icmp->icmp_cksum=in_cksum((u_short *)icmp,len);
    
        sendto(sockfd,sendbuf,len,0,pr->sasend,pr->salen);//send data
    }
    
    void readloop(void){
        int i=0;
        int size;
        char recvbuf[BUFSIZE];  //get the response
        char controlbuf[BUFSIZE];
        struct msghdr msg;
        struct iovec iov;       //send data
        ssize_t n;
        struct timeval tval;
    
        sockfd=socket(pr->sasend->sa_family,SOCK_RAW,pr->icmpproto);
    
        //set effective uid to real uid
        setuid(getuid());
        if(pr->finit)
            (*pr->finit)();
    
        size=60*1024;
        setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size));//set recvbuf size
    
        sig_alarm(SIGALRM);//cal fun
    
        //init buffer
        iov.iov_base=recvbuf;   //set recvbuf
        iov.iov_len=sizeof(recvbuf);//recvbuf len
        msg.msg_name=pr->sarecv;//sockaddr
        msg.msg_iov=&iov;
        msg.msg_iovlen=1;
        msg.msg_control=controlbuf;
    
        //loop ping
        for(;;){
            msg.msg_namelen=pr->salen;
            msg.msg_controllen=sizeof(controlbuf);
            n=recvmsg(sockfd,&msg,0);   //receive data
            if(n<0){
                if(errno=EINTR)
                    continue;
                else
                    printf("recvmsg error\n");
            }
            gettimeofday(&tval,NULL);//receive time
            (*pr->fproc)(recvbuf,n,&msg,&tval);//handle receive data
        }
    }
    

  • 相关阅读:
    Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task(枚举/最大连续子序列)
    Educational Codeforces Round 88 (Rated for Div. 2) A. Berland Poker(数学)
    Educational Codeforces Round 88 (Rated for Div. 2) E. Modular Stability(数论)
    Educational Codeforces Round 88 (Rated for Div. 2) C. Mixing Water(数学/二分)
    Codeforces Round #644 (Div. 3)
    Educational Codeforces Round 76 (Rated for Div. 2)
    Educational Codeforces Round 77 (Rated for Div. 2)
    Educational Codeforces Round 87 (Rated for Div. 2)
    AtCoder Beginner Contest 168
    Codeforces Round #643 (Div. 2)
  • 原文地址:https://www.cnblogs.com/aLittleBitCool/p/2165423.html
Copyright © 2011-2022 走看看