Skip to content

Instantly share code, notes, and snippets.

@douxxtech
Created April 24, 2026 08:29
Show Gist options
  • Select an option

  • Save douxxtech/3a1ffdf1acf6b3c1dcf81aad55a4bfe6 to your computer and use it in GitHub Desktop.

Select an option

Save douxxtech/3a1ffdf1acf6b3c1dcf81aad55a4bfe6 to your computer and use it in GitHub Desktop.
A simple traceroute implementation in C using raw ICMP sockets.
/*
A simple traceroute implementation in C using raw ICMP sockets.
Resolves hostnames, detects loops, and prints RTT for each hop.
Linux only | requires cap_net_raw or root | gcc -o traceroute traceroute.c
https://gist.github.com/douxxtech/3a1ffdf1acf6b3c1dcf81aad55a4bfe6
*/
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#define MAX_HOPS 100
#define TIMEOUT_SEC 1
#define PROBE_COUNT 3
#define BUF_SIZE 512
char *getIp(char *host)
{
// get the host entry for the hostname/IP (fetches the IP if not alr one)
struct hostent *he = gethostbyname(host);
if (he == NULL)
{
printf("Could not resolve host: %s\n", host);
exit(1);
}
struct in_addr *addr = (struct in_addr *)he->h_addr; // he points to raw IP bytes
char *ip = inet_ntoa(*addr); // convert the bytes to a string
return ip;
}
char *getHostname(char *ip)
{
struct sockaddr_in sa;
sa.sin_family = AF_INET;
inet_aton(ip, &sa.sin_addr);
static char hostname[256]; // static so it survives the return
int resolved = getnameinfo((struct sockaddr *)&sa, sizeof(sa), hostname, sizeof(hostname), NULL, 0, 0);
if (resolved == 0)
return hostname;
else
return NULL;
}
void build_echo(char *buf, int seq)
{
struct icmphdr *icmp = (struct icmphdr *)buf;
icmp->type = ICMP_ECHO;
icmp->code = 0;
icmp->un.echo.id = htons(getpid() & 0xFFFF);
icmp->un.echo.sequence = htons(seq);
icmp->checksum = 0;
// simple checksum
unsigned short *ptr = (unsigned short *)buf;
unsigned int sum = 0;
for (int i = 0; i < (int)(sizeof(struct icmphdr) / 2); i++)
sum += ptr[i];
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
icmp->checksum = ~sum;
}
double ping(int send_sock, int recv_sock, char *recv_buf, struct sockaddr_in *dest, struct sockaddr_in *from, socklen_t *from_len)
{
struct timespec start, end;
char send_buf[sizeof(struct icmphdr)];
build_echo(send_buf, 0);
clock_gettime(CLOCK_MONOTONIC, &start);
sendto(send_sock, send_buf, sizeof(send_buf), 0, (struct sockaddr *)dest, sizeof(*dest));
int received = recvfrom(recv_sock, recv_buf, BUF_SIZE, 0, (struct sockaddr *)from, from_len);
clock_gettime(CLOCK_MONOTONIC, &end);
if (received < 0)
return -1;
return (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_nsec - start.tv_nsec) / 1e6;
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("An hostname/IP address is required.\n");
exit(1);
}
char *host = argv[1];
char *ip = getIp(host);
if (strcmp(host, ip))
printf("\nTracing route to %s [%s]\nover a maximum of %d hops:\n\n", host, ip, MAX_HOPS);
else
{
char *hostname = getHostname(ip);
if (hostname != NULL && strcmp(ip, hostname))
printf("\nTracing route to %s [%s]\nover a maximum of %d hops:\n\n", hostname, ip, MAX_HOPS);
else
printf("\nTracing route to %s over a maximum of %d hops\n\n", ip, MAX_HOPS);
}
// Create sockets
int send_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
int recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (send_sock < 0 || recv_sock < 0)
{
printf("Failed to create sockets. Try running as root or set the capability:\n");
printf("sudo setcap cap_net_raw+ep %s\n", argv[0]);
exit(1);
}
struct timeval tv = {.tv_sec = TIMEOUT_SEC, .tv_usec = 0};
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); // Set timeout to not get stuck
// setup the dest
struct sockaddr_in dest;
dest.sin_family = AF_INET; // IPv4
inet_aton(ip, &dest.sin_addr); // build the IP bytes
double ms[PROBE_COUNT];
uint32_t seen_ips[MAX_HOPS] = {0};
int consecutive = 0;
for (int ttl = 1; ttl <= MAX_HOPS; ttl++)
{
// Set the TTL to the current iter ttl
setsockopt(send_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
char buf[BUF_SIZE];
struct sockaddr_in from;
socklen_t from_len = sizeof(from);
for (int i = 0; i < PROBE_COUNT; i++)
ms[i] = ping(send_sock, recv_sock, buf, &dest, &from, &from_len);
// print stats
printf("%3d ", ttl);
int erm = 0;
for (int i = 0; i < PROBE_COUNT; i++)
{
int final_ms = (int)ms[i];
if (final_ms < 0)
{
printf(" * ");
erm++;
}
else
{
if (final_ms == 0)
printf("%5s ms ", "<1");
else
printf("%5d ms ", final_ms);
}
}
if (erm == PROBE_COUNT)
{
printf("Request timed out.\n");
continue;
}
char *hop_ip = inet_ntoa(from.sin_addr);
char hostname[256];
// loop detection, see notes.txt
if (from.sin_addr.s_addr == seen_ips[ttl > 1 ? ttl - 2 : 0])
consecutive++;
else
consecutive = 0;
seen_ips[ttl - 1] = from.sin_addr.s_addr;
if (consecutive >= 2)
{
printf("%s\n", hop_ip);
printf("\nLoop detected: %s repeating. Stopping.\n", hop_ip);
break;
}
int resolved = getnameinfo((struct sockaddr *)&from, sizeof(from), hostname, sizeof(hostname), NULL, 0, 0);
if (resolved == 0 && strcmp(hostname, hop_ip))
printf("%s [%s]\n", hostname, hop_ip);
else
printf("%s\n", hop_ip);
// parse the buffer into an actual ICMP response
struct iphdr *ip_hdr = (struct iphdr *)buf;
struct icmphdr *icmp_hdr = (struct icmphdr *)(buf + ip_hdr->ihl * 4);
if (icmp_hdr->type == ICMP_ECHOREPLY && from.sin_addr.s_addr == dest.sin_addr.s_addr)
break;
}
// cleanup properly
close(send_sock);
close(recv_sock);
printf("\nTraceroute completed.\n");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment