LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 586|回复: 0

关于icmp报文的问题

[复制链接]
发表于 2008-12-6 22:13:31 | 显示全部楼层 |阅读模式
各位大虾请帮忙,小弟在linux下写了个ping程序,编译能够成功,但是运行就是问题,有时能通,有时不能通,总是解决不了,源程序以及运行结果帖如下:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
//#include <syslib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <setjmp.h>


//#define ICMP_HEADSIZE 8
//#define IP_HEADSIZE 20
#define datalen 56

#define MAX(a,b)  ((a)>(b))?(a)b)
#define MIN(a,b)  ((a)>(b))?(b)a)

/*typedef struct tagIpHead
{
  u_char   ip_verlen;
  u_char   ip_tos;
  u_char   ip_len;
  u_char   ip_id;
  u_char   ip_flagoff;
  u_char   ip_ttl;
  u_char   ip_proto;
  u_short  ip_chksum;
  u_long   ip_src_addr;
  u_long   ip_dst_addr;
}IPHEAD;

typedef struct tagIcmpHead
{
  u_char icmp_type;
  u_char icmp_code;
  u_short icmp_chksum;
  u_short icmp_id;
  u_short icmp_seq;
  u_char icmp_data[1];
}ICMPHEAD;*/

/*=====================================================
======================================================*/
u_short ChkSum(u_short *pIcmpData,int iDataLen)
{
  u_short iSum;
  u_short iOldByte=0;
  unsigned short *w=pIcmpData;
  int len=iDataLen;
  iSum=0;
  
  while(len>1)
    {
      iSum +=*w++;
      len-=2;
    }
  
  if(len==1)
    {
      *(u_char*)(&iOldByte)=*(u_char*)w;
      iSum+=iOldByte;
    }
  iSum=(iSum>>16)+(iSum&0xffff);
  iSum+=(iSum>>16);
  iOldByte=~iSum;
  printf("chsum is %d\n",iOldByte);
  return(iOldByte);
}

/*=====================================================
=====================================================*/
long time_now()
{
  struct timeval now;
  long lPassed;
  gettimeofday(&now,0);
  lPassed=now.tv_sec*1000000 + now.tv_usec;//化为微妙计算
  return lPassed;
}
int time=1;
char * host;
char * prog;
extern errno;
long lSendTime;
u_short seq;
//int iTimeOut;
int sock,sent=0,recvd;
float max,min,total;
u_long lHostIp;
char buf[200];//发送缓冲区
char buf1[200];//接收缓冲区
struct sockaddr_in it;
struct sockaddr_in from;
pid_t pid;
int fromlen=sizeof(from);
int ping();
void stat();
main(int argc,char ** argv)
{
  struct hostent*h;
  //char buf1[200];
  char dst_host[32];
  int i,namelen;
  int flag;
  struct ip* pIpHead;
  struct icmp* pIcmpHead;
  struct protoent *protocol;
  int size1=50*1024;
  pid=getpid();

  if(argc<2)
    {
      printf("usage:%s[-timeout]host}IP\n",argv[0]);
      exit(0);
    }
  prog=argv[0];
  host=argc==2?argv[1]:argv[2];
  //iTimeOut=argc==2?1:atoi(argv[1]);
  if((protocol=getprotobyname("icmp"))==NULL)
    {
      perror("getprotobyname");
      exit(1);
    }
  if((sock=socket(AF_INET,SOCK_RAW,protocol->p_proto))<0)
    {
      perror("socket");
      exit(2);
    }
  setuid(getuid);
  setsockopt(sock,SOL_SOCKET,SO_RCVBUF,&size1,sizeof(size1));

  bzero(&it,sizeof(it));
  it.sin_family=AF_INET;
  
  if((lHostIp=inet_addr(host))!=INADDR_NONE)
    {
      it.sin_addr.s_addr=lHostIp;
      strcpy(dst_host,host);
    }
  else if(h=gethostbyname(host))
    {
      bcopy(h->h_addr,&it.sin_addr,h->h_length);
      sprintf(dst_host,"%s(%s)",host,inet_ntoa(it.sin_addr));
    }
  else
    {
      fprintf(stderr,"bad IP or host\n");
      exit(3);
    }
  namelen=sizeof(it);
  printf("\nDigger pinging %s,send %d bytes\n",dst_host,datalen);
  seq=0;
  sigset(SIGINT,&stat);
  for(;;)
    {
      register  int  size;//寄存器变量占用单独的一个寄存器,这样频繁使用该寄存器相对于读取内存来说效率就提高了 , 该类型变量只有int和char变量
      register u_char ttl;
      float  delta;
      register iIpHeadLen;
      int len1;
      
     ping();
     size=recvfrom(sock,buf1,sizeof(buf1),0,(struct sockaddr *)&from,&fromlen);
     if(size==-1 && errno==EINTR)
        {
          continue;
        }
      delta=(float)((time_now()-lSendTime)/1000.0);
   
      pIpHead=(struct ip*)buf1;
      iIpHeadLen=(int)((pIpHead->ip_hl&0x0f)<<2);//ip_hl中存放的是IP头部的长度,以4字节为单位。所以需要右移2位。
      len1=size-iIpHeadLen;
   
      if(len1<8)
        {
          printf("ICMP packets length is less than 8\n");
          return (-1);
        }
   
      ttl=pIpHead->ip_ttl;
      pIcmpHead=(struct icmp*)(buf1+iIpHeadLen);//越过IP报头,直接访问ICMP报头
   
      if(pIcmpHead->icmp_type!=ICMP_ECHOREPLY)
        {
          fprintf(stderr,"i will continue for ICMPHEAD->type\n");
          continue;
        }
     
      if(pIcmpHead->icmp_id!=pid || pIcmpHead->icmp_seq!=seq)
        {
          fprintf(stderr,"i will continue for ICMPHEAD->id/seq\n");
          continue;
        }
      sprintf(buf1,"icmp_seq=%u bytes=%d ttl=%d",pIcmpHead->icmp_seq,len1,ttl);
      fprintf(stderr,"reply from %s:%s time=%f ms\n",host,buf1,delta);
      max=MAX(delta,max);
      min=min?MIN(delta,min):delta;
      total+=delta;
      ++recvd;
      ++seq;
      printf("and now seq is %d\n",seq);
    }
}

/*======================================================================
  ======================================================================*/
ping()
{
  //char buf[200];
  int iPacketSize;
  struct timeval *tval;
  long data=20;
  
  struct icmp*pIcmpHead1=(struct icmp*)buf;
  pIcmpHead1->icmp_type=ICMP_ECHO;
  pIcmpHead1->icmp_code=0;
  pIcmpHead1->icmp_id=pid;
  pIcmpHead1->icmp_seq=seq;
  pIcmpHead1->icmp_cksum=0;
  fprintf(stderr,"but icmp_seq is %d\n",seq);
  *((long*)pIcmpHead1->icmp_data)=data;
  //tval=(struct timeval *)pIcmpHead1->icmp_data;
  //gettimeofday(tval,NULL);
  iPacketSize=datalen+8;
  pIcmpHead1->icmp_cksum=ChkSum((u_short*)pIcmpHead1,iPacketSize);
  lSendTime=time_now();
  if(sendto(sock,buf,iPacketSize,0,(struct sockaddr*)&it,sizeof(it))<0)
    {
      perror("send failed");
      exit(6);
    }
  printf("this time is No.%d\n",time++);
  sleep(1);
  ++sent;

}

/*================================================================
  =================================================================*/
void stat()
{
  if(sent)
    {
      printf("\n----%s ping statistics summerized by Digger----\n",host);
      printf("%d packets sent,%d packets received,%.2f%%lost\n",sent,recvd,(float)(sent-recvd)/(float)sent*100);
    }
  if(recvd)
    {
      printf("round_trip min/avg/max:%f/%f/%f ms\n\n",min,total/recvd,max);
    }
  exit(0);
}
运行结果:
letueo@letueo:/usr/local/program$ sudo ./ping letueo

Digger pinging letueo(127.0.1.1),send 56 bytes
but icmp_seq is 0
chsum is 59363
this time is No.1
i will continue for ICMPHEAD->type
but icmp_seq is 0
chsum is 59363
this time is No.2
reply from letueo:icmp_seq=0 bytes=64 ttl=64 time=1000.153015 ms
and now seq is 1
but icmp_seq is 1
chsum is 59362
this time is No.3
i will continue for ICMPHEAD->type
but icmp_seq is 1
chsum is 59362
this time is No.4
i will continue for ICMPHEAD->id/seq
but icmp_seq is 1
chsum is 59362
this time is No.5
i will continue for ICMPHEAD->type
but icmp_seq is 1
chsum is 59362
this time is No.6
reply from letueo:icmp_seq=1 bytes=64 ttl=64 time=1000.166016 ms
and now seq is 2
but icmp_seq is 2
chsum is 59361
this time is No.7

----letueo ping statistics summerized by Digger----
6 packets sent,2 packets received,66.67%lost
round_trip min/avg/max:1000.153015/1000.159546/1000.166016 ms
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表