Traceroute - 2 본문
ICMP 패킷을 보내고 다시 받은 패킷의 src IP를 추출했다.
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
// ICMP 체크섬 계산 함수
unsigned short checksum(void *b, int len) {
unsigned short *buf = (unsigned short *)b;
unsigned int sum = 0;
unsigned short result;
for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(unsigned char *)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
int main() {
int sockfd;
struct sockaddr_in dest_addr;
struct icmphdr icmp_hdr;
char packet[64];
//소켓 생성
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) {
perror("socket");
return 1;
}
int ttl = 1;
if (setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
perror("setsockopt");
close(sockfd);
return 1;
}
// 목적지 주소 설정
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr("223.130.192.247"); // naver 서버
// ICMP 헤더 설정
memset(&icmp_hdr, 0, sizeof(icmp_hdr));
icmp_hdr.type = ICMP_ECHO;
icmp_hdr.un.echo.id = getpid();
icmp_hdr.un.echo.sequence = 1;
icmp_hdr.checksum = checksum(&icmp_hdr, sizeof(icmp_hdr));
// 패킷에 ICMP 헤더 복사
memcpy(packet, &icmp_hdr, sizeof(icmp_hdr));
// ICMP 에코 요청 전송
if (sendto(sockfd, packet, sizeof(icmp_hdr), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) <= 0) {
perror("sendto");
return 1;
}
int pid = getpid();
std::cout << "ICMP Echo Request sent to www.naver.com" << std::endl;
std::cout << "Process ID : " << std::hex << pid << std::endl;
memset(packet, 0, sizeof(icmp_hdr));
struct sockaddr_in recv_addr;
socklen_t addr_len = sizeof(recv_addr);
int bytes_recived = recvfrom(sockfd, packet, sizeof(icmp_hdr), 0, (struct sockaddr *)&recv_addr, &addr_len);
if(bytes_recived <= 0){
perror("recvfrom");
close(sockfd);
return 1;
}
struct iphdr *ip_hdr = (struct iphdr *)packet;
struct icmphdr *recv_icmp_hdr = (struct icmphdr *)(packet + (ip_hdr->ihl * 4));
std::cout << "Received ICMP packet with type " << (int)recv_icmp_hdr->type << std::endl;
std::cout << "ICMP Echo Reply received from " << inet_ntoa(recv_addr.sin_addr) << std::endl;
close(sockfd);
return 0;
}
먼저 TTL을 설정을 해주어야 한다.
default가 64이기 때문에 ICMP패킷을 보내면 결과가 돌아오지 않는다.
현재는 1만 보내서 패킷이 수신되는지 확인을 해준다.
int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
해당 함수를 통해 ttl을 설정했다.
- sockfd: 소켓 디스크립터이다.
- IPPROTO_IP: IP 수준에서 옵션을 설정한다.
- IP_TTL: 설정할 옵션이 TTL임을 지정한다.
- &ttl: TTL 값에 대한 포인터를 전달한다.
- sizeof(ttl): TTL 값의 크기를 전달한다.
memset(packet, 0, sizeof(icmp_hdr));
struct sockaddr_in recv_addr;
socklen_t addr_len = sizeof(recv_addr);
int bytes_recived = recvfrom(sockfd, packet, sizeof(icmp_hdr), 0, (struct sockaddr *)&recv_addr, &addr_len);
if(bytes_recived <= 0){
perror("recvfrom");
close(sockfd);
return 1;
}
패킷을 받는 구문이다.
ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
recvfrom의 원형이다.
- socket: 데이터를 수신할 소켓 디스크립터. 여기서는 sockfd이다.
- buffer: 수신된 데이터를 저장할 버퍼. 여기서는 packet이다.
- length: 수신할 데이터의 최대 크기. 여기서는 sizeof(packet)이다.
- flags: 수신 동작을 제어하는 플래그. 일반적으로 0을 사용한다.
- src_addr: 데이터를 보낸 주소를 저장할 구조체. 여기서는 recv_addr이다.
- addrlen: src_addr 구조체의 크기를 나타내는 변수의 포인터. 여기서는 &addr_len이다.
struct iphdr *ip_hdr = (struct iphdr *)packet;
struct icmphdr *recv_icmp_hdr = (struct icmphdr *)(packet + (ip_hdr->ihl * 4));
2개의 변수를 만들어준다.
난해할 수도 있는데 먼저 수신받은 packet을 iphdr 구조체로 타입캐스팅해서 넘겨준다.
iphdr의 구조체는
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
unsigned int ihl:4;
unsigned int version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
unsigned int version:4;
unsigned int ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
u_int8_t tos;
u_int16_t tot_len;
u_int16_t id;
u_int16_t frag_off;
u_int8_t ttl;
u_int8_t protocol;
u_int16_t check;
u_int32_t saddr;
u_int32_t daddr;
/*The options start here. */
};
이렇게 생겼다.
이 형식에 맞게 데이터가 들어가게 된다.
ICMP 헤더는 IP헤더의 20byte 이후 데이터를 가져오면 된다.
즉
(struct icmphdr *)(packet + (ip_hdr->ihl * 4))
ip_hdr->ih1은 IP헤더의 DWORD 단위로 알려준다.
더블 워드는 32bit이다. 32bit는 4byte이므로 4를 곱해준다.
그리고 packet시작 주소와 함께 더해주면 그 주소가 ICMP 헤더의 시작 주소이다.
TTL을 1로 설정하고 실행을 해보면은
192.168.73.2에 도달한다는 것을 알 수 있다.
이게 내 가상머신의 게이트웨이이다.
'개인 프로젝트 공부' 카테고리의 다른 글
Upbit trading bot - 1 (0) | 2024.07.15 |
---|---|
Traceroute - 3(완) (0) | 2024.07.14 |
Traceroute - 1 (0) | 2024.07.08 |
TFT Support - 5(완) (0) | 2024.07.05 |
TFT Support - 4 (0) | 2024.07.03 |
Comments