• 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
        }
    }
    

  • 相关阅读:
    微信小程序登入实现
    CacheLab实验--深入了解计算机系统实验
    power designer的物理数据模型生成数据字典。
    PowerDesigner15在生成SQL时报错Generation aborted due to errors detected during the verification of the mo
    Mac系统下,如何申请并安装教育版Navicat
    Mac下修改Mysql密码
    数组
    AbstractList源码阅读
    List源码阅读笔记
    AbstractCollection源码阅读笔记
  • 原文地址:https://www.cnblogs.com/aLittleBitCool/p/2165423.html
Copyright © 2020-2023  润新知