Compare commits

..

7 Commits

Author SHA1 Message Date
c6289081e7 add: error message 2025-11-20 10:37:16 -06:00
214abd7ddd clean: move packet header (type and code) verifications from packet_compare to packet_check 2025-11-19 09:15:34 -06:00
d902a4b48a fix: condition loop 2025-11-19 09:14:20 -06:00
3a3b4279ad add: preload 2025-11-19 09:12:20 -06:00
bd66c1c27e clean: remove flood 2025-11-19 08:33:06 -06:00
3acc6d084e add: implement ttl 2025-11-19 08:32:03 -06:00
af9758f462 fix: print stats recved / transmited 2025-11-19 08:31:30 -06:00
8 changed files with 239 additions and 89 deletions

View File

@ -24,9 +24,13 @@ int dns_lookup(struct hostenv *host)
} }
struct addrinfo *first = responses; struct addrinfo *first = responses;
struct in_addr *addr = &(((struct sockaddr_in *) first->ai_addr)->sin_addr);
inet_ntop(first->ai_family, addr, host->ipstr, sizeof(char) * INET_ADDRSTRLEN); host->ip.sin_addr.s_addr = ((struct sockaddr_in *) first->ai_addr)->sin_addr.s_addr;
host->ip.sin_port = ((struct sockaddr_in *) first->ai_addr)->sin_port;
memcpy(host->ip.sin_zero, ((struct sockaddr_in *) first->ai_addr)->sin_zero, sizeof(host->ip.sin_zero));
host->ip.sin_family = ((struct sockaddr_in *) first->ai_addr)->sin_family;
inet_ntop(first->ai_family, &host->ip.sin_addr, host->ipstr, sizeof(char) * INET_ADDRSTRLEN);
freeaddrinfo(responses); freeaddrinfo(responses);

87
src/icmp_error.h Normal file
View File

@ -0,0 +1,87 @@
#pragma once
typedef enum
{
ICMP_ECHOREPLY = 0, /* Echo Reply */
ICMP_DEST_UNREACH = 3, /* Destination Unreachable */
ICMP_SOURCE_QUENCH = 4, /* Source Quench */
ICMP_REDIRECT = 5, /* Redirect (change route) */
ICMP_ECHO = 8, /* Echo Request */
ICMP_TIME_EXCEEDED = 11, /* Time Exceeded */
ICMP_PARAMETERPROB = 12, /* Parameter Problem */
ICMP_TIMESTAMP = 13, /* Timestamp Request */
ICMP_TIMESTAMPREPLY = 14, /* Timestamp Reply */
ICMP_INFO_REQUEST = 15, /* Information Request */
ICMP_INFO_REPLY = 16, /* Information Reply */
ICMP_ADDRESS = 17, /* Address Mask Request */
ICMP_ADDRESSREPLY = 18 /* Address Mask Reply */
} net_icmp_types;
typedef enum
{
ICMP_NET_UNREACH = 0, /* Network Unreachable */
ICMP_HOST_UNREACH = 1, /* Host Unreachable */
ICMP_PROT_UNREACH = 2, /* Protocol Unreachable */
ICMP_PORT_UNREACH = 3, /* Port Unreachable */
ICMP_FRAG_NEEDED = 4, /* Fragmentation Needed/DF set */
ICMP_SR_FAILED = 5, /* Source Route failed */
ICMP_NET_UNKNOWN = 6,
ICMP_HOST_UNKNOWN = 7,
ICMP_HOST_ISOLATED = 8,
ICMP_NET_ANO = 9,
ICMP_HOST_ANO = 10,
ICMP_NET_UNR_TOS = 11,
ICMP_HOST_UNR_TOS = 12,
ICMP_PKT_FILTERED = 13, /* Packet filtered */
ICMP_PREC_VIOLATION = 14, /* Precedence violation */
ICMP_PREC_CUTOFF = 15 /* Precedence cut off */
} net_icmp_unreach_codes; /* Codes for UNREACH*/
typedef enum
{
ICMP_REDIR_NET = 0,
ICMP_REDIR_HOST = 1,
ICMP_REDIR_NETTOS = 2,
ICMP_REDIR_HOSTTOS = 3
} net_icmp_redirect_code; /* Codes for REDIRECT*/
typedef enum
{
ICMP_EXC_TTL = 0,
ICMP_EXC_FRAGTIME = 1
} net_icmp_time_code; /* Codes for TIME_EXCEEDED. */
static const char *net_icmp_unreach_messages[] =
{
[ICMP_NET_UNREACH] = "Network Unreachable",
[ICMP_HOST_UNREACH] = "Host Unreachable",
[ICMP_PROT_UNREACH] = "Protocol Unreachable",
[ICMP_PORT_UNREACH] = "Port Unreachable",
[ICMP_FRAG_NEEDED] = "Fragmentation Needed/DF set",
[ICMP_SR_FAILED] = "Source Route failed",
[ICMP_NET_UNKNOWN] = "Network Unknown",
[ICMP_HOST_UNKNOWN] = "Host Unknown",
[ICMP_HOST_ISOLATED] = "Host Isolated",
[ICMP_NET_ANO] = "Network Administratively Prohibited",
[ICMP_HOST_ANO] = "Host Administratively Prohibited",
[ICMP_NET_UNR_TOS] = "Network Unreachable for TOS",
[ICMP_HOST_UNR_TOS] = "Host Unreachable for TOS",
[ICMP_PKT_FILTERED] = "Packet filtered",
[ICMP_PREC_VIOLATION] = "Precedence violation",
[ICMP_PREC_CUTOFF] = "Precedence cut off"
};
static const char *net_icmp_redirect_messages[] =
{
[ICMP_REDIR_NET] = "Redirect for Network",
[ICMP_REDIR_HOST] = "Redirect for Host",
[ICMP_REDIR_NETTOS] = "Redirect for Network with TOS",
[ICMP_REDIR_HOSTTOS] = "Redirect for Host with TOS"
};
static const char *net_icmp_time_messages[] =
{
[ICMP_EXC_TTL] = "Time-to-live exceeded",
[ICMP_EXC_FRAGTIME] = "Fragment Reassembly Timeout"
};

View File

@ -31,7 +31,7 @@
bool loop = true; bool loop = true;
void signal_handler(int code) static void signal_handler(int code)
{ {
(void) code; (void) code;
loop = false; loop = false;
@ -69,36 +69,86 @@ value_error:
return 2; return 2;
} }
static int get_setting(char * const *av, struct setting *setting) static int get_setting(char * const *av, struct setting *settings)
{ {
struct param parameters[] = { struct param parameters[] = {
{NULL, "?", OPTION, false}, {NULL, "?", OPTION, false},
{NULL, "v", OPTION, false}, {NULL, "v", OPTION, false},
{NULL, "f", OPTION, false},
{NULL, "n", OPTION, false}, {NULL, "n", OPTION, false},
{NULL, "W",ARGUMENT, "0"},
{"ttl", NULL, ARGUMENT, "116"}, {"ttl", NULL, ARGUMENT, "116"},
{NULL, "s", ARGUMENT, "56"}, {NULL, "s", ARGUMENT, "56"},
{NULL, "p", ARGUMENT, "0"},
{NULL, NULL, 0, NULL}, {NULL, NULL, 0, NULL},
}; };
char *hostname = parsing(av, parameters); char *hostname = parsing(av, parameters);
if (hostname == NULL) if (hostname == NULL)
return 1; return 1;
setting->dest.hostname = hostname; settings->dest.hostname = hostname;
setting->help = parameters[0].value; settings->help = parameters[0].value;
setting->verbose = parameters[1].value; settings->verbose = parameters[1].value;
setting->flood = parameters[2].value; settings->numeric_only = parameters[2].value;
setting->numeric_only = parameters[3].value; size_t ttl;
if (parsing_number(parameters[4].value, 0, 2147483647, &setting->timeout)) if (parsing_number(parameters[3].value, 0, 255, &ttl))
return 1;
if (parsing_number(parameters[5].value, 0, 255, &setting->ttl))
return 2; return 2;
if (parsing_number(parameters[6].value, 0, 2147483647, &setting->payload_size)) settings->ttl = ttl;
if (parsing_number(parameters[4].value, 0, 2147483647, &settings->payload_size))
return 3; return 3;
if (parsing_number(parameters[5].value, 0, 2147483647, &settings->preload))
return 4;
return 0; return 0;
} }
static int create_socket(struct setting const *settings)
{
int ret;
(void)settings;
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd == -1)
{
print_err("open socket failed, maybe try in root (:");
return -1;
}
ret = setsockopt(sockfd, IPPROTO_IP, IP_TTL, &settings->ttl, sizeof(uint8_t));
if (ret != 0)
{
print_err("Failed to setsockopt(): ttl");
return -1;
}
struct timeval tv = {1, 0};
ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if (ret != 0)
{
print_err("Failed to setsockopt(): recv timeout");
return -1;
}
return sockfd;
}
static int preload(struct setting const *settings, int sockfd, char const *packet, size_t packet_size)
{
for (size_t i = 0; i < settings->preload; i++)
{
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings->dest.ip, sizeof(settings->dest.ip)) == -1)
return 1;
}
return 0;
}
int check_reply(struct sockaddr_in const *me, struct sockaddr_in const *sender, char *sent, char *received, size_t packet_size)
{
if (((struct icmphdr *)sent)->type != 0)
return 0;
if (check_packet_conformity(sent, received, packet_size))
return 1;
return memcmp(sender, me, sizeof(struct sockaddr_in));
}
int main(int ac, char **av) int main(int ac, char **av)
{ {
(void) ac; (void) ac;
@ -106,51 +156,41 @@ int main(int ac, char **av)
struct setting settings; struct setting settings;
if (get_setting(av + 1, &settings)) if (get_setting(av + 1, &settings))
return 1; goto error0;
size_t packet_size = sizeof(struct icmphdr) + settings.payload_size; size_t packet_size = sizeof(struct icmphdr) + settings.payload_size;
size_t recv_packet_size = packet_size + sizeof(struct iphdr);
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
if (dns_lookup(&settings.dest)) if (dns_lookup(&settings.dest))
return 5; goto error0;
settings.dest.ip.sin_family = AF_INET;
settings.dest.ip.sin_port = htons(0);
inet_pton(AF_INET, settings.dest.ipstr, &settings.dest.ip.sin_addr);
dns_reverse_lookup(&settings.dest);
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
int sockfd = create_socket(&settings);
if (sockfd == -1) if (sockfd == -1)
{ goto error0;
print_err("open socket failed, maybe try in root (:");
return 1;
}
char *packet = packet_create(settings.payload_size); char *packet = packet_create(settings.payload_size);
if (packet == NULL) if (packet == NULL)
{ goto error1;
close(sockfd);
return 2;
}
char *buffer; char *buffer;
buffer = malloc(packet_size * sizeof(char)); buffer = malloc((recv_packet_size) * sizeof(char));
if (buffer == NULL) if (buffer == NULL)
{ {
print_err("error: allocation failed."); print_err("error: allocation failed.");
free(packet); goto error2;
close(sockfd);
return 10;
} }
struct sockaddr_in sender; struct sockaddr_in sender;
socklen_t len = sizeof(sender); socklen_t len = sizeof(sender);
bzero(&stats, sizeof(struct statistics)); bzero(&stats, sizeof(struct statistics));
print_header(&settings); print_header(&settings);
if (preload(&settings, sockfd, packet, packet_size))
goto error3;
while (loop) { while (loop) {
struct timeval stop, start; struct timeval stop, start;
@ -159,45 +199,48 @@ int main(int ac, char **av)
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings.dest.ip, sizeof(settings.dest.ip)) == -1) if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings.dest.ip, sizeof(settings.dest.ip)) == -1)
{ {
print_err("error: send packet failed."); print_err("error: send packet failed.");
free(packet); goto error2;
close(sockfd);
return 2;
} }
stats.packets_sent++; stats.packets_sent++;
ssize_t ret = 0;
do do
{ {
if (recvfrom(sockfd, buffer, packet_size, 0, (struct sockaddr *) &sender, &len) < 0) ret = recvfrom(sockfd, buffer, recv_packet_size, 0, (struct sockaddr *) &sender, &len);
if (ret < 0)
{ {
print_err("error: receive packet failed."); print_err("error: receive packet failed.");
close(sockfd); goto error3;
free(buffer);
free(packet);
return 3;
} }
gettimeofday(&stop, NULL); gettimeofday(&stop, NULL);
} }
while (len == packet_size && packet_check(buffer, packet_size) == 0 && packet_compare(packet, buffer, packet_size) == 0); while (((size_t) ret != recv_packet_size) || (check_reply(&settings.dest.ip, &sender, packet, buffer + sizeof(struct iphdr), packet_size)));
if (((struct icmphdr *) buffer + sizeof(struct iphdr))->type == 0)
stats.packets_received++; stats.packets_received++;
struct icmphdr *hdr = (struct icmphdr *) packet; print_recv(&settings, (struct icmphdr*) (buffer + sizeof(struct iphdr)), &start, &stop, &sender);
print_ping(&settings, hdr, &start, &stop);
sleep(1); sleep(1);
if (packet_update(packet, settings.payload_size)) if (packet_update(packet, settings.payload_size))
{ goto error3;
close(sockfd);
free(buffer);
free(packet);
}
} }
free(packet); free(packet);
free(buffer); free(buffer);
close(sockfd);
print_statistics(&stats, &settings); print_statistics(&stats, &settings);
return 0;
error3:
free(buffer);
error2:
free(packet);
error1:
close(sockfd);
error0:
return 1;
} }

View File

@ -2,6 +2,8 @@
#include <fcntl.h> #include <fcntl.h>
#include <netinet/ip_icmp.h> #include <netinet/ip_icmp.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -26,8 +28,8 @@ static int fill_with_random(char *str, size_t nb)
return 0; return 0;
} }
static unsigned short checksum(void *b, int len) { static unsigned short checksum(void const *b, int len) {
unsigned short *buf = b; unsigned short const *buf = b;
unsigned int sum = 0; unsigned int sum = 0;
unsigned short result; unsigned short result;
@ -41,15 +43,20 @@ static unsigned short checksum(void *b, int len) {
return result; return result;
} }
int packet_compare(const char *sent, const char *received, size_t packet_size) int check_packet_conformity(char const *sent, char const *received, size_t packet_size)
{ {
const struct icmphdr *sent_hdr = (const struct icmphdr*) sent; struct icmphdr const *sent_hdr = (struct icmphdr const *) sent;
const struct icmphdr *received_hdr = (const struct icmphdr*) received; struct icmphdr *received_hdr = (struct icmphdr *) received;
if (received_hdr->type != 0 || received_hdr->code != 8) if (received_hdr->un.echo.sequence != sent_hdr->un.echo.sequence)
return 1; return 1;
if (received_hdr->un.echo.sequence == sent_hdr->un.echo.sequence) const uint16_t checksum_bak = received_hdr->checksum;
received_hdr->checksum = 0;
const uint16_t tmp = checksum(received, packet_size);
received_hdr->checksum = checksum_bak;
if (checksum_bak != tmp)
return 2; return 2;
const char *sent_payload = sent + sizeof(struct icmphdr); const char *sent_payload = sent + sizeof(struct icmphdr);
@ -58,22 +65,9 @@ int packet_compare(const char *sent, const char *received, size_t packet_size)
return memcmp(sent_payload, received_payload, packet_size - sizeof(struct icmphdr)); return memcmp(sent_payload, received_payload, packet_size - sizeof(struct icmphdr));
} }
int packet_check(char *packet, size_t packet_size)
{
struct icmphdr *hdr = (struct icmphdr *) packet;
const uint16_t checksum_bak = hdr->checksum;
hdr->checksum = 0;
int tmp = checksum(packet, packet_size);
hdr->checksum = checksum_bak;
return tmp - checksum_bak;
}
int packet_update(char *packet, size_t payload_size) int packet_update(char *packet, size_t payload_size)
{ {
static size_t sequence = 1; static uint16_t sequence = 0;
size_t hdr_size = sizeof(struct icmphdr); size_t hdr_size = sizeof(struct icmphdr);
size_t packet_size = hdr_size + payload_size; size_t packet_size = hdr_size + payload_size;

View File

@ -2,7 +2,6 @@
#include <stddef.h> #include <stddef.h>
int packet_compare(const char *sent, const char *received, size_t packet_size); int check_packet_conformity(char *sent, char *received, size_t packet_size);
int packet_check(char *packet, size_t packet_size);
int packet_update(char *packet, size_t payload_size); int packet_update(char *packet, size_t payload_size);
void *packet_create(size_t payload_size); void *packet_create(size_t payload_size);

View File

@ -1,8 +1,10 @@
#include "icmp_error.h"
#include "interval.h" #include "interval.h"
#include "setting.h" #include "setting.h"
#include "statistics.h" #include "statistics.h"
#include <arpa/inet.h>
#include <bits/types/struct_timeval.h> #include <bits/types/struct_timeval.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip.h> #include <netinet/ip.h>
@ -45,18 +47,37 @@ void print_statistics(struct statistics const *stats, struct setting const *sett
printf("%zu packets transmitted, %zu packets received, %.3f%% packet loss\n", printf("%zu packets transmitted, %zu packets received, %.3f%% packet loss\n",
stats->packets_sent, stats->packets_sent,
stats->packets_received, stats->packets_received,
((double) stats->packets_sent) * 100 / (double) stats->packets_received (double) stats->packets_received * 100 / (double) stats->packets_sent
); );
// TODO fix packet lost calcul
} }
void print_recv(const struct setting *settings, const struct icmphdr *header, const struct timeval *start, const struct timeval *stop) static const char *get_message_description(const uint8_t type, const uint8_t code)
{ {
switch(type)
{
case 0:
return "icmp_seq=%d ttl=%d time=%.3f ms";
case ICMP_DEST_UNREACH:
return (net_icmp_unreach_messages[code]);
case ICMP_REDIRECT:
return (net_icmp_redirect_messages[code]);
case ICMP_TIME_EXCEEDED:
return (net_icmp_time_messages[code]);
}
return (NULL);
}
printf("%zu bytes from %s: icmp_seq=%d ttl=%zu time=%.3f ms\n", void print_recv(const struct setting *settings, const struct icmphdr *header, const struct timeval *start, const struct timeval *stop, struct sockaddr_in const *sender)
settings->payload_size + sizeof(struct icmphdr), {
settings->dest.ipstr, char *ipstr = inet_ntoa(sender->sin_addr);
printf("%zu", settings->payload_size + sizeof(struct icmphdr) + ((header->type == 0) ? 0 : sizeof(struct iphdr)));
printf(" bytes from %s: ", ipstr);
printf(get_message_description(header->type, header->code),
htons(header->un.echo.sequence), htons(header->un.echo.sequence),
settings->ttl, settings->ttl,
get_interval(start, stop) get_interval(start, stop)
); );
printf("\n");
} }

View File

@ -9,4 +9,4 @@ void print_err(const char *format, ...);
void print_header(struct setting const *settings); void print_header(struct setting const *settings);
void print_statistics(struct statistics const *stats, struct setting const *settings); void print_statistics(struct statistics const *stats, struct setting const *settings);
void print_recv(struct setting const *settings, struct icmphdr const *header, struct timeval const *start, struct timeval const *stop); void print_recv(const struct setting *settings, const struct icmphdr *header, const struct timeval *start, const struct timeval *stop, struct sockaddr_in const *sender);

View File

@ -2,6 +2,7 @@
#include "host.h" #include "host.h"
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
@ -12,7 +13,8 @@ struct setting
bool flood; bool flood;
bool numeric_only; bool numeric_only;
size_t timeout; size_t timeout;
size_t ttl; uint8_t ttl;
size_t payload_size; size_t payload_size;
struct hostenv dest; struct hostenv dest;
size_t preload;
}; };