티스토리 뷰

설명 

목적 서버에 대한 ICMP(:12)에 대한 응답시간을 체크한다.

사용방법 

int main(int argc, char **argv)
{
	int sockfd;
	int ip;
	struct timeval atime;
	struct sockaddr_in addr; 
	sockfd = IcmpInit();
	ip = CheckIcmp(sockfd, argv[1], &atime, &addr);
	printf("%s ---> %d sec %d msec\n", inet_ntoa(addr.sin_addr), 
				atime.tv_sec, atime.tv_usec);
	IcmpClear(sockfd);	
}

위의 코드를 컴파일한 후 실행시킨 결과다.
# ./icmp_check www.joinc.co.kr
218.234.19.87 ---> 0 sec 8 msec

int IcmpInit();
  1. ICMP 프로토콜을 사용하는 RAW 소켓을 작성한다.
  2. return : 열린 소켓지정자를 리턴한다. 


int CheckIcmp(int sockfd, 
			char *dstaddr, 
			struct timeval *atime, 
			struct sockaddr_in *ipaddr)
  1. 목적지 주소로 ICMP ECHO를 전송하고 리턴을 검사한다.
  2. sockfd : IcmpInit()를 통해서 리턴된 소켓지정자
  3. dstaddr : 조사하고자 하는 목적지의 인터넷 주소
  4. atime : 응답 시간 조사, atime.tv_sec (초), atime.tv_usec(밀리세컨드)
  5. ipaddr : 클라이언트(ECHO REPLAY)호스트 주소
  6. return : 실패했을 경우 -1을 리턴 

int IcmpClear();
  1. 소켓을 닫고 자원을 해제한다. 

코드 

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

// IP CheckSum 생성 
int in_cksum(u_short *p, int n)
{
    register u_short answer;
    register long sum = 0;
    u_short odd_byte = 0;

    while(n > 1)
    {
        sum += *p++;
        n -= 2;
    }

    if (n == 1)
    {
        *(u_char *)(&odd_byte) = *(u_char *)p;
        sum += odd_byte;    
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;

    return (answer);
}

// ICMP 프로토콜을 지원하는 RAW소켓 생성
int IcmpInit()
{
    int icmp_socket;
    icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    return icmp_socket;
}

int CheckIcmp(int sockfd, 
			char *dstaddr, 
			struct timeval *atime, 
			struct sockaddr_in *ipaddr)
{
    struct icmp *p, *rp;
    char buffer[1024];
    int sl, ret, hlen;
	int diff;
    struct sockaddr_in addr, sin, from;
	struct ip *ip;
	struct timeval ltime;
	struct hostent *hentry;

    // 호스트 이름으로 부터 주소갑을 얻어온다.
	if ((hentry = gethostbyname(dstaddr)) == NULL)
	{
		return -1;
	}

	gettimeofday(&ltime, NULL);
    memset(buffer, 0x00, 1024);
    p = (struct icmp *)buffer;
    p->icmp_type = ICMP_ECHO;
    p->icmp_code = 0;
    p->icmp_cksum = 0;
    p->icmp_seq = 15; 
    p->icmp_id = getpid();
	p->icmp_dun.id_ts.its_otime = ((ltime.tv_sec%100)*10000)+(ltime.tv_usec / 100); 
    p->icmp_cksum = in_cksum((u_short *)p, 1000);
	memcpy(buffer+sizeof(*p), (void *)&time, 4);

    memset(&addr, 0, sizeof(0));
	memcpy(&addr.sin_addr, hentry->h_addr, hentry->h_length);
    addr.sin_family = AF_INET;

    ret = sendto(sockfd, p, sizeof(*p), MSG_DONTWAIT, 
                    (struct sockaddr *)&addr, sizeof(addr)); 
    if (ret < 0)
	{
		exit(0);
        return -1;
	}
    sl = sizeof(from);
    ret = recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr *)&from, &sl); 

    if (ret < 0)
        return -1;
    ip = (struct ip *)buffer;
    hlen = ip->ip_hl*4;
    rp = (struct icmp *)(buffer+hlen);
	gettimeofday(&ltime, NULL);
	diff = (((ltime.tv_sec%100)*10000)+ltime.tv_usec/100) - rp->icmp_dun.id_ts.its_otime;
	atime->tv_sec = diff/1000; 
	atime->tv_usec = diff/10; 
	*ipaddr = addr;
    return sin.sin_addr.s_addr;
}

int IcmpClear(int sockfd)
{
	close(sockfd);
	return 1;
}

해야될 것 

서버가 일정시간 응답하지 않을경우 에러를 발생하고 리턴하도록 코드를 수정해야 한다. 소켓 지정자에 대해 select()를 이용하면 될것이다. 입력에 대한 select()관련 코드는 read_wait를 참고하기 바란다.


출처 :
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Code/C/icmp_check