ping命令实现内幕

在网络上有不少ping命令的实现,但是要么连运行都不行,要么就是表层的侥幸的能成功一些例子。今我自己写了个ping命令程序,自习研究了一下,原来问题颇多。。。。。。 很多还得看底层协议代码才能搞清楚它的来龙去脉。。。 先将调试版ping命令程序代码贴上:

/***********************ping程序,completed by ben****************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFSIZE 1024
#define MAX_PACK_NUM
char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE];
//int buflen=128;
int datalen=56;
unsigned short pid;
struct sockaddr *sasend;
struct sockaddr *sarecv;
struct sockaddr_in *dst;
char abuf[INET_ADDRSTRLEN];
int sckadlen;
static unsigned short nsend=0;
int sockfd;
int recvnum=0;
int totaltime=0;
struct timeval sendtime,endtime;


//static int sendnum=0;

unsigned short cksum(unsigned short *addr ,int len)
{
 int nleft=len;
 unsigned int sum=0;
 unsigned short *w=addr;
 unsigned short 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;
}

void sendpkg()

 //printf("icmp len is %d/n",sizeof(struct icmp));
 int flag=0,n;
 //memset(sendbuf+64,0,sizeof(sendbuf)-64);
 memset(sendbuf,0,sizeof(sendbuf));//要初始化,原因是校验和那个域初始时要清零,就这个地方我调了很久,原来不知道为什么要

                                                            //初始化,因为后面都已经重新赋值了。
 //memset(sendbuf,1,64);
 //memset(sendbuf,0,64);
 //memset(sendbuf,0xbb,sizeof(sendbuf));
 //memset(sendbuf+2,0,2);
 //for( n=0;n<64;n++)
  // { 
    //a=(char)sendbuf[n];
  //  printf("sendbuf byte %d is %x/n",n,sendbuf[n]);
  // }
 
 struct icmp *icmp;
 icmp=(struct icmp *)sendbuf;
 icmp->icmp_type=ICMP_ECHO;
 icmp->icmp_code=0;
 icmp->icmp_seq=htons(nsend++);
 //icmp->icmp_seq=htons(0);
 //printf("seq is %d/n",icmp->icmp_seq);
 
 //printf("icmp_type is %x/n",&icmp->icmp_type);

 //printf("icmp_seq is %x/n",&icmp->icmp_seq);
 //printf("icmp->icmp_id is %x/n",&icmp->icmp_id);
 //printf("icmp len is %d/n",sizeof(struct icmp));
 //printf("icmp_data is %x/n",&icmp->icmp_data);

 //printf("pid is %d/n",pid);
 //icmp->icmp_id=htons(pid);
 icmp->icmp_id=htons(pid);
 //printf("icmp->icmp_id is %x/n",icmp->icmp_id);
 //printf("sendbuf byte 4 is %d/n",sendbuf[4]);
 //int n;
 memset(icmp->icmp_data,0xa5,datalen);
 
 gettimeofday((struct timeval *)icmp->icmp_data,NULL);
 struct timeval *temptival=(struct timeval *)icmp->icmp_data;
 temptival->tv_sec=ntohl(temptival->tv_sec);
 temptival->tv_usec=ntohl(temptival->tv_usec);
 
 
 if(nsend==1) gettimeofday(&sendtime,NULL);
 //if(flag==0) {flag++;gettimeofday(&sendtime,NULL);} 
 int len=8+datalen;
 //printf("heheheheh/n");
 icmp->icmp_cksum=htons(cksum((unsigned short *)icmp,len));
 //icmp->icmp_cksum=cksum((unsigned short *)icmp,len);
 //printf("sasend->sa_len is %d/n",sasend->sa_family);
 //sleep(1);
 unsigned char a=8;
  //printf("a is %x/n",a);
 //for(n=0;n<64;n++)
  // { 
  //  a=(char)sendbuf[n];
   // printf("sendbuf byte %d is %x/n",n,(char)a);
   //}
 if((n=sendto(sockfd,sendbuf,len,0,sasend,sckadlen))<0)
  { 
   perror("error is:");
   //printf("error is %s/n", hstrerror(h_errno));
   printf("send error:%d/n",errno);
   exit(-1);
  }
 //printf("n is %d /n",n);
 return;
}

void sig_alrm(int signo)

 //if(sendnum>=3) return;
 sendpkg();
 alarm(1);
 //sendnum++;
 return;
}


void tv_sub(struct timeval *out,struct timeval *in)
{       if( (out->tv_usec-=in->tv_usec)<0)
        {       --out->tv_sec;
                out->tv_usec+=1000000;
        }
        out->tv_sec-=in->tv_sec;
}

void catchint(int signo)

 //echo();
 //nocbreak();
 //endwin();
 gettimeofday(&endtime,NULL);
 //printf("endtime.tv_sec is %u/n",endtime.tv_sec);
 //printf("endtime.tv_usec is %u/n",endtime.tv_usec);
 //printf("endtime.tv_sec is %u/n",sendtime.tv_sec);
 //printf("endtime.tv_usec is %u/n",sendtime.tv_usec);
 
 tv_sub(&endtime,&sendtime);
 //printf("endtime.tv_sec is %u/n",endtime.tv_sec);
 //printf("endtime.tv_usec is %u/n",endtime.tv_usec);
 totaltime=endtime.tv_sec*1000+endtime.tv_usec/1000;
 //printf("hssh is %d/n",totaltime);
 int a=(nsend-recvnum)*100/((double)nsend);
 printf("/n---www.a.shifen.com ping statistics ---/n");
 printf("%d packets transmitted,%d recerved,%d%% packets loss,time %dms/n",
     nsend,recvnum,a,totaltime);
 signal(SIGINT,SIG_DFL);
 exit(0);
}


void recvpkg()

 int n,iph_len,icmplen;
 struct timeval tval,*tvsend;
 struct ip *ip;
 struct icmp *icmp;
 int len;
 len=sizeof(struct sockaddr);
 double rtt;
 sarecv=calloc(1,sizeof(struct sockaddr));
 //int num;
 //for(num=0;num<3;num++)
 for(;;)
 {  
   //sleep(1);
   //alarm(1);
   //printf("in recvpkg/n");
   memset(recvbuf,0,sizeof(recvbuf));
   //sarecv=calloc(1,sizeof(struct sockaddr));//内存泄漏
   n=recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,sarecv,&len);
   //printf("len is %d/n",len);
   //printf("sarecv->sa_data is %s/n",sarecv->sa_data);
   if(n<0)
   {
    if(errno==EINTR)
     continue;
    else
    exit(-1);
   }
   //printf("n is %d/n",n);
   //printf("error when recv/n");
   //exit(-1);
   //gettimeofday(tval,NULL);
   ip=(struct ip*)recvbuf;
   iph_len=ip->ip_hl<<2;
   
   //printf("hehe/n");
   
   if(ip->ip_p!=IPPROTO_ICMP)
   return;
   //printf("hododo/n");
   icmp=(struct icmp*)(recvbuf+iph_len);
   icmplen=n-iph_len;
   if(icmplen<8) continue;
   //printf("jkjk/n");
   
    if(icmp->icmp_type==ICMP_ECHOREPLY&&ntohs(icmp->icmp_id)==pid)
   {
    if(icmplen<16)
     continue;
    recvnum++;
    tvsend=(struct timeval*)icmp->icmp_data;
    tvsend->tv_sec=ntohl(tvsend->tv_sec);
    tvsend->tv_usec=ntohl(tvsend->tv_usec);
    gettimeofday(&tval,NULL);//原来这里tval为空指针,造成非法访问内存
    tv_sub(&tval,tvsend);
    rtt=tval.tv_sec*1000.0+tval.tv_usec/1000.0;
    printf("%d bytes from %s:seq=%u,ttl=%d,rtt=%3.f ms/n",
        icmplen,abuf,ntohs(icmp->icmp_seq),ip->ip_ttl,rtt);
   }
   else
    continue;
 }
 free(sarecv); 
 printf("out of circle /n");
 return;
}

int main(int argc,char **argv)

 
 //initscr();
 //cbreak();
 //noecho();
 //struct hostent *host;
 char *host;
 char *h=NULL;
 //char abuf[INET_ADDRSRTLEN];
 if(argc==1)
 {
  printf("usage:./ping host_name/n");
  exit(-1);
 }
 struct addrinfo hints,*res;
 struct sockaddr_in *dst;
 bzero(&hints,sizeof(struct addrinfo));
 hints.ai_flags=AI_CANONNAME;
 hints.ai_family=AF_UNSPEC;//0
 hints.ai_socktype=0;
 host=argv[1];
 signal(SIGALRM,sig_alrm);
 signal(SIGINT,catchint);
 pid=getpid();
 printf("hi,pid is %d/n",pid);
 if(getaddrinfo(host,NULL,&hints,&res)!=0)
 {
  printf("please check the host name,getting the host failed");
  exit(-1);
 }
 sasend=res->ai_addr;
 sckadlen=res->ai_addrlen;
 //printf("sckadlen is %d/n",sckadlen);
 dst=(struct sockaddr_in *)res->ai_addr;//dst=(sockaddr_in *)&res->ai_addr;
 //sasend=dst
 h=inet_ntop(AF_INET,&dst->sin_addr,abuf,INET_ADDRSTRLEN);
 if(h==NULL)
 {
  printf("error:/n");
  exit(-1);
 }
 printf("h is %s/n",h);
 printf("PING %s(%s):%d data bytes/n",res->ai_canonname?res->ai_canonname:h,h,datalen);
 //int protocol;
 struct protoent *protocol;
 if( (protocol=getprotobyname("icmp") )==NULL)
        {       perror("getprotobyname");
                exit(1);
        }
 sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto); //IPPROTO_ICMP
 int buflen=BUFSIZE;
 //printf("come to here:/n");
 setsockopt(AF_INET,SOL_SOCKET,SO_RCVBUF,&buflen,sizeof(buflen));
 sig_alrm(SIGALRM);
 //sendpkg();
 recvpkg();
 return 1;

 

 

对方接受到ICMP数据包在ip层还会做处理:

当一个I C M P报文到达时,I P层通过i n e t s w [ 4 ]的p r _ i n p u t
函数,间接调用i c m p _ i n p u t函数。

icmp_input->in_cksum():这个函数会重新计算校验和,如果校验和不对,接受到的包含ICMP报文IP包将就不会被发送。

你可能感兴趣的:(struct,null,signal,statistics,byte,socket)