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

    }

}

 

你可能感兴趣的:(linux)