C언어 icmp, ping 구현
C언어를 이용한 icmp, ping 을 구현하였다.
이따끔 network target의 alive 상태를 체크해야할 일이 있다.
Rest API 요청이라던가 TCP session 개시 라던가 뭔가 network request 상황에서 system time_wait, fin_wait, response block 등 여러 상황에 빠지는 상황을 만들지 않으려면 애초에 안보내는게 제일 깔끔하기 때문이다.
그래서 icmp check를 한 후 정상적인 상황에서만 network 적인 작업을 수행한다.
소스코드
- TIME_LOOP_WAIT는 200ms 간격으로 icmp를 체크하겠단 의미다.
- COUNT_CHECK_LOOP는 5회 동안 체크하겠단 의미다.
즉, 한번에 응답이 오지 않는 상황(e.g. 서버는 정상이나 부하상태 또는 네트워크 오류 등)에 alive check를 retry 하는 동작을 수행한단 의미이다.
5회 수행 * 200밀리초 간격은 즉 최대 1초까지 기다려 보겠단 것이다.
필요한 만큼 수정해서 사용하자.
별다른 수정 없이 아래 함수를 사용하면 즉시 동작 가능하며 C++ 코드가 있는 건 단순히
boolean type과 string class를 사용하고 싶기 때문이다. C++적인 요소를 치환하면 완벽한 C코드로 동작 가능하다.
#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <string>
#define TIME_LOOP_WAIT 200000 // 200ms
#define COUNT_CHECK_LOOP 5
#define SIZE_ICMP_PACKET 64
using namespace std;
struct ICMP_PACKET {
struct icmphdr hdr;
char msg[SIZE_ICMP_PACKET - sizeof(struct icmphdr)];
} typedef ICMP_PACKET_t;
/* functions */
unsigned short checksum(void* _data, int _length) {
unsigned short* p_data;
unsigned short result;
unsigned int sum = 0;
p_data = (unsigned short*)_data;
for (sum = 0; _length > 1; _length -= 2) {
sum += *p_data++;
}
if( _length == 1 ) {
sum += *(unsigned char*)p_data;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
bool icmp(string _address) {
const int sock_value = 255;
int socket_fd;
int num_sequence = 1;
int pid = getpid();
int idx;
struct sockaddr_in r_addr;
struct hostent *hname;
struct sockaddr_in addr_ping,*addr;
ICMP_PACKET_t pckt;
int size_packet_msg = sizeof(pckt.msg);
struct protoent *t_proto = NULL;
socklen_t len;
t_proto = getprotobyname("ICMP");
hname = gethostbyname(_address.c_str());
bzero(&addr_ping, sizeof(addr_ping));
addr_ping.sin_family = hname->h_addrtype;
addr_ping.sin_port = 0;
addr_ping.sin_addr.s_addr = *(long*)hname->h_addr;
addr = &addr_ping;
if( (socket_fd = socket(PF_INET, SOCK_RAW, t_proto->p_proto)) < 0 ) {
printf("icmp() socket open failed : [%02d] %s\n", errno, strerror(errno));
return false;
}
if( setsockopt(socket_fd, SOL_IP, IP_TTL, &sock_value, sizeof(sock_value)) != 0) {
printf("icmp() set TTL option failed : [%02d] %s\n", errno, strerror(errno));
return false;
}
if ( fcntl(socket_fd, F_SETFL, O_NONBLOCK) != 0 ) {
printf("icmp() request nonblocking I/O failed : [%02d] %s\n", errno, strerror(errno));
return false;
}
for( int loop_cnt = 0 ; loop_cnt < COUNT_CHECK_LOOP ; loop_cnt++ ) {
len = sizeof(r_addr);
if( recvfrom(socket_fd, &pckt, sizeof(pckt), 0x00, (struct sockaddr*)&r_addr, &len) > 0 ) {
close(socket_fd);
return true;
}
bzero(&pckt, sizeof(pckt));
pckt.hdr.type = ICMP_ECHO;
pckt.hdr.un.echo.id = pid;
for( idx = 0; idx < size_packet_msg - 1 ; idx++ ) {
pckt.msg[idx] = idx + '0';
}
pckt.msg[idx] = 0;
pckt.hdr.un.echo.sequence = num_sequence++;
pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
if( sendto(socket_fd, &pckt, sizeof(pckt), 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0 ) {
printf("icmp() sendto failed : [%02d] %s\n", errno, strerror(errno));
}
usleep(TIME_LOOP_WAIT);
}
close(socket_fd);
return false;
}
/* main function */
int main(int _argc, char *_argv[]) {
// target: google DNS
if( icmp("8.8.8.8") ) {
printf("icmp() status : [success]\n");
} else {
printf("icmp() status : [failed]\n");
}
return 0;
}
결과
테스트는 전세상 사람들이 다 알고 있는 google DNS인 8.8.8.8로 해보자. 내가 죽어 백골이 진토되는 순간이 와도 google DNS는 죽지 않고 영원할 것이다.
muabow@muabow:~/dev/cpp_file$ g++ -o icmp icmp.cpp ; sudo ./icmp
icmp() status : [success]
따란 당연하게도 성공.
위 코드는 도메인명을 사용할 수 없다.(e.g. http://google.com) 당연하게도 name resolve 를 하는 코드가 없기 때문이다.
힌트는 줬으니 필요하면 domain -> ip address 를 바꾸는 코드만 추가해서 사용하자.
끝.
'IT > programming' 카테고리의 다른 글
Rest API 요청 언어별 정리, CURL/PHP/PYTHON/C/C++/QT/JAVA/Node.js (6) | 2022.01.17 |
---|---|
[C/C++] C언어 MAC 주소 읽기 구현, get mac address (0) | 2022.01.16 |
[C/C++] select 를 활용한 non-block I/O, FD_SET, FD_ISSET (0) | 2022.01.14 |
[C/C++] 전역 네임스페이스, 시스템 함수와 동일한 클래스 메서드 이름 사용 방법 (0) | 2022.01.14 |
[C/C++] C++ curl handler class 예제, C언어 curl 구현 (10) | 2022.01.13 |
댓글