用socket编程实现Ping程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <netinet/tcp.h>
#include<stdlib.h>
#include<sys/time.h>
#include <time.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<netinet/ip.h>

#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int

struct icmphdr
{
    u8 type;
    u8 code;
    u16 checksum;
    union
    {
        struct
        {
            u16 id;
            u16 sequence;
        }echo;
        
        u32 gateway;
        struct
        {
            u16 unused;
            u16 mtu;
        }frag; //pmtu发现
    }un;
    
    //u32  icmp_timestamp[2];//时间戳
    //ICMP数据占位符
    u8 data[0];
#define icmp_id un.echo.id
#define icmp_seq un.echo.sequence
};

#define NETWORK_UNREACHABLE (-3)
#define ICMP_ECHOREPLY              0     /*Echo Reply*/
#define ICMP_DEST_UNREACH       3     /*Destination Unreachable*/
#define ICMP_SOURCE_QUENCH    4     /*Source Quench */
#define ICMP_REDIRECT          5     /*Redirect (change route)*/
#define ICMP_ECHO           8     /*Echo Request*/
#define ICMP_TIME_EXCEEDED     11    /*Time Exceeded*/
#define ICMP_PARAMETERPROB     12    /*Parameter Problem*/
#define ICMP_TIMESTAMP        13    /*Timestamp Request*/
#define ICMP_TIMESTAMPREPLY    14    /*Timestamp Reply*/
#define ICMP_INFO_REQUEST  15    /*Information Request*/
#define ICMP_INFO_REPLY              16    /*Information Reply*/
#define ICMP_ADDRESS           17    /*Address Mask Request*/
#define ICMP_ADDRESSREPLY 18    /*Address Mask Reply*/
#define NR_ICMP_TYPES          18

struct timeval tv_interval,tv_recv, tv_send;

/*计算end和begin的时戳差*/
struct timeval IcmpTvsub(struct timeval end, struct timeval begin)
{
	struct timeval tv;
	tv.tv_sec = end.tv_sec - begin.tv_sec;
	tv.tv_usec = end.tv_usec - begin.tv_usec;
	if (tv.tv_usec < 0)
	{
		tv.tv_sec --;
		tv.tv_usec += 1000000;
	}
	return tv;
}

/*计算累加和*/
int CheckSum(uint16_t *addr, int len)
{
    int nleft = len;
    uint16_t *w = addr;
    int sum = 0;
    uint16_t answer = 0;

    /*
     * Our algorithm is simple, using a 32 bit accumulator (sum), we add
     * sequential 16 bit words to it, and at the end, fold back all the
     * carry bits from the top 16 bits into the lower 16 bits.
     */
    while (nleft > 1)
    {
		sum += *w++;
		nleft -= 2;
    }

    /* mop up an odd byte, if necessary */
    if (nleft == 1)
    {
		*(uint16_t *)(&answer) = *(uint16_t *)w ;
		sum += answer;
    }

    /* add back carry outs from top 16 bits to low 16 bits */
    sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
    sum += (sum >> 16);			/* add carry */
    answer = (~sum);				/* truncate to 16 bits */
    return(answer); 
}

/*发送ICMP request包*/
int SendIcmpPack(int sockfd, unsigned int dst_ip)
{
	static unsigned long seq = 0;
    struct sockaddr_in to_ip;
    unsigned long saddr = htonl(dst_ip);
    struct icmphdr *head;
    uint8_t outpack[64];
    int cs = 64 ;
    int ret;
    struct in_addr addr;
	int ident = getpid( ) & 0xFFFF;

    /*填充远端地址结构体*/
    bzero(&to_ip, sizeof(struct sockaddr_in));
    bzero(outpack, sizeof(outpack));
    to_ip.sin_family = AF_INET;
    to_ip.sin_addr.s_addr = saddr;

    /*制作ICMP包头*/
    head = (struct icmphdr *)outpack;
    head->type = ICMP_ECHO;
    head->code = 0;
    head->checksum = 0;
    head->un.echo.sequence = htons(++seq);;
    head->un.echo.id = ident;
    head->checksum = CheckSum((uint16_t *)head, cs);
	gettimeofday(&tv_send,NULL);
    /*发送ICMP报文*/
    ret = sendto(sockfd, (char *)outpack, cs, 0, (struct sockaddr *)&to_ip, sizeof(struct sockaddr_in));
    if (ret < 0)
    {
		printf("sendto icmp packet error:%s\r\n", strerror(errno));
		return -1;
    }
    return 0;
}

/*接收ICMP response包*/
int RecvIcmpPack(int sockfd, unsigned short timeout , int dst_ip)
{
	struct timeval tv;
	int rtt;
	fd_set rfd;
	struct sockaddr_in from_ip;
	struct ip *ip=NULL;
	int dst_addr = htonl(dst_ip);
	int from_len = sizeof(struct sockaddr_in);
	int ident = getpid( ) & 0xFFFF;
	struct icmphdr *head;
	uint8_t inpack[128];
	int ret;
	unsigned int orig_time = time(NULL);
    unsigned int current_time = 0;
    unsigned int timeout_dval = 0;
	
select_again:
	current_time = time(NULL);
    timeout_dval = current_time - orig_time;
    if (timeout <= timeout_dval)
    {
        printf("recv_icmp_pack:select time out\n");
        return -2;
    }
    timeout -= timeout_dval;
	
	bzero(&from_ip, sizeof(struct sockaddr_in));
	bzero(inpack, sizeof(inpack));

	tv.tv_sec = timeout;
	tv.tv_usec = 0;

	FD_ZERO(&rfd);
	FD_SET(sockfd, &rfd);

	ret = select(sockfd + 1, &rfd, NULL, NULL, &tv);
	if (ret < 0)
	{
		if (errno == EINTR)
		{
			goto select_again;
		}
		return -1;
	}
	
	if (ret == 0)
	{
		return -2;
	}

	/*接收ICMP响应报文*/
	ret = recvfrom(sockfd, (char *)inpack, sizeof(inpack), 0, (struct sockaddr *)&from_ip, &from_len);
	if (ret < 0)
	{
		printf("recv error: %s\r\n", strerror(errno));
		return -1;
	}
	else if (ret == 0)
	{
		printf("recv datalen is zero\r\n");
		return -1;
	}
	
    if(0 != memcmp(&dst_addr,&from_ip.sin_addr.s_addr,4))
    {
        goto select_again;
    }
	
	head = (struct icmphdr *)(inpack + 20);
	ip= (struct ip*)inpack;   
	if (head->type == ICMP_ECHOREPLY)
	{
		if ((head->un.echo.id == ident))
		{
			gettimeofday(&tv_recv,NULL);
			tv_interval = IcmpTvsub(tv_recv,tv_send);
			rtt = tv_interval.tv_sec*1000+tv_interval.tv_usec/1000;
			printf("recv %d byte from%s: icmp_seq=%u ttl=%d rtt=%d ms\n",ret,inet_ntoa(*(struct in_addr*)&dst_addr),ntohs(head->icmp_seq),ip->ip_ttl,rtt);
			return 0;
		}
		else
		{
			printf("recv echo.id is incorrect(%d,%d)\n", head->un.echo.id, ident);
			goto select_again;
		}

	}
	else if(head->type==ICMP_DEST_UNREACH)
    {
        printf("recv destination unreachable\n");
        return NETWORK_UNREACHABLE;
    }
    else if(head->type==ICMP_ECHO)
    {
        if (head->un.echo.id == ident)
        {
            printf("recv error !!!  head->type = %d\n",head->type);
        }
        else
        {
            goto select_again;
        }
    }	
	else
	{
		goto select_again;
	}
	printf("recv data type is wrong\r\n");
    
	return -1;    
}

/*Icmp检测*/
int CheckIcmp(int sockfd , unsigned int dst_ip)
{
	int ret;
	struct in_addr ipdata;
	
	ipdata.s_addr = htonl(dst_ip);
	
	ret = SendIcmpPack(sockfd, dst_ip);

	if (ret < 0)
	{
		printf("send icmp pack error\n");
		return -1;		
	}
	else
	{
		printf("*****ICMP send one packet success(%s)!*****\n", inet_ntoa(ipdata));
	}
	
	ret = RecvIcmpPack(sockfd, 5 ,dst_ip);

	if (ret < 0)
	{
		if (ret == -1)
		{	      
			printf("ICMP recv icmp packet error\n");
		}
		else
		{
			printf("ICMP Recv icmp packet timeout\n");
		}

		return -1;
	}
	else
	{
		printf("*****ICMP resv one packet success!*****\n");
	}
	return 0;
}

/*传入参数为域名(或者IP)*/
int main(int argc, char*argv[])
{
	int ret;
	int fd;
	unsigned int ip;
	struct hostent *host;
	
	if (2 > argc)
	{
		printf("please input the ping dstip\n");
		return -1;
	}

    //解析域名
	host = gethostbyname(argv[1]);
	if(!host)
	{
		printf("Get IP address error!");
		return -1;
	}
	//别名
	for(int i=0; host->h_aliases[i]; i++)
	{
		printf("Aliases %d: %s\n", i+1, host->h_aliases[i]);
	}
	//地址类型
	printf("Address type: %s\n", (host->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");
	//IP地址
	for(int i=0; host->h_addr_list[i]; i++)
	{
		printf("IP addr %d: %s\n", i+1, inet_ntoa( *(struct in_addr*)host->h_addr_list[i] ) );
	}
	
	inet_aton(inet_ntoa( *(struct in_addr*)host->h_addr_list[0] ), (struct in_addr*)&ip);
	ip = ntohl(ip);
	fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (0 > fd)
	{
		printf("create socket error\n");
		return -1;
	}
	while(1)
	{
		ret = CheckIcmp(fd, ip);
		if (0 > ret)
		{
			return -1;
		}
		sleep(1);
	}
	
}

下面运行结果

mcchen@mcchen-virtual-machine:/home/samba/share/mywork/test/icmp$ sudo gcc -o icmp icmp.c 
mcchen@mcchen-virtual-machine:/home/samba/share/mywork/test/icmp$ sudo ./icmp www.baidu.com
Address type: AF_INET
IP addr 1: 14.215.177.39
IP addr 2: 14.215.177.38
*****ICMP send one packet success(14.215.177.39)!*****
recv 84 byte from14.215.177.39: icmp_seq=1 ttl=56 rtt=8 ms
*****ICMP resv one packet success!*****
*****ICMP send one packet success(14.215.177.39)!*****
recv 84 byte from14.215.177.39: icmp_seq=2 ttl=56 rtt=23 ms
*****ICMP resv one packet success!*****
*****ICMP send one packet success(14.215.177.39)!*****
recv 84 byte from14.215.177.39: icmp_seq=3 ttl=56 rtt=10 ms
*****ICMP resv one packet success!*****
*****ICMP send one packet success(14.215.177.39)!*****
recv 84 byte from14.215.177.39: icmp_seq=4 ttl=56 rtt=8 ms
*****ICMP resv one packet success!*****
*****ICMP send one packet success(14.215.177.39)!*****
recv 84 byte from14.215.177.39: icmp_seq=5 ttl=56 rtt=7 ms
*****ICMP resv one packet success!*****
发布了33 篇原创文章 · 获赞 7 · 访问量 8339

猜你喜欢

转载自blog.csdn.net/muchong123/article/details/103484989