Board logo

标题: icmp协议 ping编程实现(2) [打印本页]

作者: yuyang911220    时间: 2015-11-23 22:03     标题: icmp协议 ping编程实现(2)

使用宏定义令表达更简洁,其中ICMP报头为8字节,数据报长度最大为64K字节。

校验和算法――这一算法称为网际校验和算法,把被校验的数据16位进行累加,然后取反码,若数据字节长度为奇数,则数据尾部补一个字节的0以凑成偶数。此算法适用于IPv4、ICMPv4、IGMPV4、ICMPv6、UDP和TCP校验和,更详细的信息请参考RFC1071,校验和字段为上述ICMP数据结构的icmp_cksum变量。
标识符――用于唯一标识ICMP报文, 为上述ICMP数据结构的icmp_id宏所指的变量。
顺序号――ping命令的icmp_seq便由这里读出,代表ICMP报文的发送顺序,为上述ICMP数据结构的icmp_seq宏所指的变量。

ICMP数据报
Ping命令中需要显示的信息,包括icmp_seq和ttl都已有实现的办法,但还缺rtt往返时间。为了实现这一功能,可利用ICMP数据报携带一个时间戳。使用以下函数生成时间戳:
#include
int gettimeofday(struct timeval *tp,void *tzp)
其中timeval结构如下:
struct timeval{
long tv_sec;
long tv_usec;
}

其中tv_sec为秒数,tv_usec微秒数。在发送和接收报文时由gettimeofday分别生成两个timeval结构,两者之差即为往返时间,即ICMP报文发送与接收的时间差,而timeval结构由ICMP数据报携带,tzp指针表示时区,一般都不使用,赋NULL值。

数据统计
系统自带的ping命令当它接送完所有ICMP报文后,会对所有发送和所有接收的ICMP报文进行统计,从而计算ICMP报文丢失的比率。为达此目的,定义两个全局变量:接收计数器和发送计数器,用于记录ICMP报文接受和发送数目。丢失数目=发送总数-接收总数,丢失比率=丢失数目/发送总数。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <setjmp.h>
#include <errno.h>

#define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3

char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int sockfd,datalen=56;
int nsend=0,nreceived=0;
struct sockaddr_in dest_addr;
pid_t pid;
struct sockaddr_in from;
struct timeval tvrecv;

void statistics(int signo);
unsigned short cal_chksum(unsigned short *addr,int len);
int pack(int pack_no);
void send_packet(void);
void recv_packet(void);
int unpack(char *buf,int len);
void tv_sub(struct timeval *out,struct timeval *in);

void statistics(int signo)
{
  printf("\n--------------------PINGstatistics-------------------\n");
   printf("%d packetstransmitted, %d received , %%%dlost\n",nsend,nreceived,(nsend-nreceived)/nsend*100);
   close(sockfd);
   exit(1);
}

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

int pack(int pack_no)
{
    inti,packsize;
    struct icmp*icmp;
    structtimeval *tval;
    icmp=(structicmp*)sendpacket;
   icmp->icmp_type=ICMP_ECHO;
   icmp->icmp_code=0;
   icmp->icmp_cksum=0;
   icmp->icmp_seq=pack_no;
   icmp->icmp_id=pid;
   packsize=8+datalen;
    tval=(struct timeval *)icmp->icmp_data;
   gettimeofday(tval,NULL);
   icmp->icmp_cksum=cal_chksum( (unsigned short*)icmp,packsize);
    returnpacksize;
}


void send_packet()
{
    intpacketsize;
   while(nsend)
    {
     nsend++;
     packetsize=pack(nsend);
     if(sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr*)&dest_addr,sizeof(dest_addr))<0)
     {
       perror("sendto error");
       continue;
     }
     sleep(1);
    }
}


void recv_packet()
{
    int n,fromlen;
    extern int errno;
    signal(SIGALRM,statistics);
    fromlen=sizeof(from);
    while(nreceived)
    {
      alarm(MAX_WAIT_TIME);
      if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(structsockaddr *)&from,&fromlen))<0)
      {
          if(errno==EINTR) continue;
          perror("recvfrom error");
          continue;
      }
      gettimeofday(&tvrecv,NULL);
      if(unpack(recvpacket,n)==-1) continue;
      nreceived++;
    }
}

int unpack(char *buf,int len)
{
    inti,iphdrlen;
    struct ip*ip;
    struct icmp*icmp;
    structtimeval *tvsend;
    doublertt;
    ip=(structip *)buf;
   iphdrlen=ip->ip_hl<<2;
    icmp=(structicmp *)(buf+iphdrlen);
   len-=iphdrlen;
   if(len<8)
    {
     printf("ICMP packets\'s length is less than 8\n");
     return -1;
    }
   
    if((icmp->icmp_type==ICMP_ECHOREPLY)&&(icmp->icmp_id==pid) )
    {
       tvsend=(struct timeval *)icmp->icmp_data;
       tv_sub(&tvrecv,tvsend);
       rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;
      
       printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",
       len,inet_ntoa(from.sin_addr),icmp->icmp_seq,ip->ip_ttl,rtt);
    }
    else return-1;
}




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0