1. 程式人生 > >ICMP->linux c PING功能實現

ICMP->linux c PING功能實現

報文格式:

\

程式碼

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip_icmp.h> //struct icmp
#include <netinet/in.h> //sockaddr_in
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>

//校驗和計算
unsigned short calc_cksum(char *buff,int len)
{
    int blen = len;
    unsigned short *mid = (unsigned short*)buff;
    unsigned short te = 0;
    unsigned int sum = 0;

    while(blen > 1)
    {
       sum += *mid++;
       blen -= 2; 
    }
    //資料長度為奇數比如65 上面的while是按16計算的 最後就會剩下一位元組不能計算 	
    if(blen == 1)
    {  
       //將多出的一位元組放入short型別的高位 低8位置0 加入到sum中
       te = *(unsigned char*)mid;
       te  = (te << 8) & 0xff;
       sum += te;                
    }
    sum = (sum >> 16) + (sum&0xffff); 
    sum += sum >>16;  
    return (unsigned short)(~sum);
}

static void icmp_packet(char *buff,int len,int id,int seq)
{
    struct timeval *tval = NULL;
    struct icmp *icmp = (struct icmp*)buff;
   
    icmp->icmp_type = 8; //ECHO REQUEST
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;  //first set zero
    icmp->icmp_id = id & 0xffff;
    icmp->icmp_seq = seq;
	
    tval = (struct timeval *)icmp->icmp_data;    
    gettimeofday(tval,NULL);//獲得傳輸時間作為資料

    //計算校驗和
    icmp->icmp_cksum = calc_cksum(buff,len);   
    return;    
}

void parse_packet(char *buff,int len)
{
    struct timeval *val;
    struct timeval nv;
    struct icmp *icmp;
    struct iphdr *iphead = (struct iphdr *)buff;
    struct in_addr addr; 
    addr.s_addr = iphead->saddr;

    printf("comefrom ip=%s  ",inet_ntoa(addr));
    //跳過ip頭
    icmp = (struct icmp *)(buff+sizeof(struct iphdr));
    
    //看傳輸回的包校驗和是否正確
    if(calc_cksum((char *)icmp,len-sizeof(sizeof(struct iphdr))) > 1)
    {
       printf("receiver error\n");
       return;
    } 
    gettimeofday(&nv,NULL);
    val = (struct timeval *)icmp->icmp_data;

    printf("type=%d  seq=%d id=%d pid=%d usec=%d \n",icmp->icmp_type,icmp->icmp_seq,icmp->icmp_id,(getpid()&0xffff),nv.tv_usec - val->tv_usec);
    
}

int main(int argc,char *argv[])
{
    int skfd;
    struct sockaddr_in addr={0};    
    struct sockaddr_in saddr={0};
    char buff[64]={0};  
    char recvbuff[512]={0};  
    int ret;
    int addrlen = 0;
    int count = 5; 
    int i = 1;
     
    skfd = socket(PF_INET,SOCK_RAW,IPPROTO_ICMP);
    if(skfd < 0)
    {
        printf("socket error\n");
        return -1;
    }
         
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("192.168.13.148");

   //每一秒傳送一次 共傳送count次    
    while(count > 0)
    {
        //序列號seq 從1 開始傳輸  buff的大小為64
        memset(buff,0,sizeof(buff));
        icmp_packet(buff,64,getpid(),i);
        i++;
        count --;

        //將資料傳送出去
        ret = sendto(skfd,buff,64,0,(struct sockaddr *)&addr,sizeof(addr)); 
        if(ret <= 0)
        {
            printf("send error\n"); 
            goto out;  
        }  
        else
           printf("send success ret=%d\n",ret);

        //接收echo replay
        memset(recvbuff,0,sizeof(recvbuff));
        memset(&saddr,0,sizeof(saddr));
        addrlen = sizeof(saddr);
        ret = recvfrom(skfd,recvbuff,sizeof(recvbuff),0,(struct sockaddr *)&saddr,&addrlen);   
        if(ret <= 0)
        {
            printf("recv error\n");
            goto out;
        }
        parse_packet(recvbuff,ret);
        sleep(1);
     }
out:
    close(skfd);
    return 0;
}