diff --git a/P8/icmp-timestamp.c b/P8/icmp-timestamp.c new file mode 100644 index 0000000000000000000000000000000000000000..011847e0f8d45c8dd0bdba3288d0b228578ec8e4 --- /dev/null +++ b/P8/icmp-timestamp.c @@ -0,0 +1,178 @@ +/** + * Practica Tema 8: ICMP-TIMESTAMP + * + * Apellido, Nombre + * Apellido, Nombre + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/time.h> +#include "ip-icmp.h" + +#define ICMP_TIMESTAMP 13 +#define ICMP_TIMESTAMP_REPLY 14 +#define TIMEOUT 5 // Tiempo de espera en segundos + +void crearICMPRequest(TimeStamp *icmp_msg); +unsigned short checksum(void *b, int len); +void imprimeError(unsigned char type, unsigned char code); + +int main(int argc, char *argv[]) { + if (argc < 2 || argc > 3) { + fprintf(stderr, "Uso: %s ip [-v]\n", argv[0]); + exit(EXIT_FAILURE); + } + + int verbose = (argc == 3 && strcmp(argv[2], "-v") == 0); + const char *ip = argv[1]; + int sockfd; + struct sockaddr_in dest_addr; + unsigned char buffer[1024]; + + // Crear socket RAW para ICMP + sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sockfd < 0) { + perror("Error al crear el socket"); + exit(EXIT_FAILURE); + } + + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + if (inet_pton(AF_INET, ip, &dest_addr.sin_addr) <= 0) { + perror("Error al convertir la dirección IP"); + exit(EXIT_FAILURE); + } + + // Construir el mensaje ICMP + TimeStamp icmp_msg; + crearICMPRequest(&icmp_msg); + + // En modo verbose, imprime siempre los detalles del datagrama enviado + if (verbose) { + printf("-> Type: %d\n", icmp_msg.icmpHdr.type); + printf("-> Code: %d\n", icmp_msg.icmpHdr.code); + printf("-> PID : %d\n", ntohs(icmp_msg.pid)); + printf("-> Seq Numbr: %d\n", ntohs(icmp_msg.sequence)); + printf("-> Originate: %u ms\n", ntohl(icmp_msg.originate)); + printf("-> Receive: 0 ms\n"); // Los campos Receive y Transmit no están en icmp_msg + printf("-> Transmit: 0 ms\n"); + printf("-> Tamanyo del Datagrama: %ld bytes\n", sizeof(TimeStamp)); + } + + // Enviar mensaje ICMP + printf("-> Enviando Datagrama ICMP a Destino con IP: %s\n", ip); + if (sendto(sockfd, &icmp_msg, sizeof(icmp_msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) { + perror("Error al enviar el datagrama"); + exit(EXIT_FAILURE); + } + printf("-> Timestamp Request enviado correctamente...\n"); + + // Configurar timeout + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(sockfd, &readfds); + + struct timeval timeout = {TIMEOUT, 0}; + if (select(sockfd + 1, &readfds, NULL, NULL, &timeout) <= 0) { + printf("-> Tiempo de espera agotado para %s\n", ip); + exit(EXIT_FAILURE); + } + + // Recibir respuesta + TimeStampReply recv_reply; + socklen_t addr_len = sizeof(dest_addr); + if (recvfrom(sockfd, &recv_reply, sizeof(recv_reply), 0, (struct sockaddr *)&dest_addr, &addr_len) < 0) { + perror("Error al recibir el datagrama"); + exit(EXIT_FAILURE); + } + + // Verificar tipo de respuesta + if (recv_reply.icmpMsg.icmpHdr.type == ICMP_TIMESTAMP_REPLY && recv_reply.icmpMsg.icmpHdr.code == 0) { + printf("\n-> Timestamp Reply recibido desde %s\n", ip); + + if (strcmp(ip, "127.0.0.1") == 0) { // Caso especial: Loopback + printf("-> ICMP Datagram Not Processed...\n"); + } else { + unsigned long originate = ntohl(recv_reply.icmpMsg.originate); + unsigned long receive = ntohl(recv_reply.icmpMsg.receive); + unsigned long transmit = ntohl(recv_reply.icmpMsg.transmit); + + struct timeval end_time; + gettimeofday(&end_time, NULL); + unsigned long t4 = ((unsigned long)end_time.tv_sec * 1000) + ((unsigned long)end_time.tv_usec / 1000); + unsigned long rtt = (receive - originate) + (t4 - transmit); + + if (verbose) { + printf("-> Originate: %lu ms\n", originate); + printf("-> Receive: %lu ms\n", receive); + printf("-> Transmit: %lu ms\n", transmit); + printf("-> RTT: %lu ms\n", rtt); + printf("-> TTL: %d\n", recv_reply.ipHdr.TTL); + printf("-> Tamanyo del Datagrama: %ld bytes\n", sizeof(TimeStampReply)); // Cambiado a TimeStampReply + } + + printf("-> Respuesta Correcta (Type %d, Code %d)\n", recv_reply.icmpMsg.icmpHdr.type, recv_reply.icmpMsg.icmpHdr.code); + } + } else { + // Caso de error genérico o inesperado + imprimeError(recv_reply.icmpMsg.icmpHdr.type, recv_reply.icmpMsg.icmpHdr.code); + } + + close(sockfd); + return EXIT_SUCCESS; +} + +// Función para inicializar el mensaje ICMP +void crearICMPRequest(TimeStamp *icmp_msg) { + memset(icmp_msg, 0, sizeof(TimeStamp)); + icmp_msg->icmpHdr.type = ICMP_TIMESTAMP; + icmp_msg->icmpHdr.code = 0; + icmp_msg->pid = htons(getpid()); + icmp_msg->sequence = htons(0); + + struct timeval start_time; + gettimeofday(&start_time, NULL); + icmp_msg->originate = htonl((start_time.tv_sec * 1000) + (start_time.tv_usec / 1000)); + + icmp_msg->icmpHdr.checksum = checksum(icmp_msg, sizeof(TimeStamp)); +} + +// Función para calcular el checksum +unsigned short checksum(void *b, int len) { + unsigned short *buf = b; + unsigned int sum = 0; + for (; len > 1; len -= 2) sum += *buf++; + if (len == 1) sum += *(unsigned char *)buf; + sum = (sum >> 16) + (sum & 0xFFFF); + return ~sum; +} + +// Función para imprimir errores ICMP +void imprimeError(unsigned char type, unsigned char code) { + const char *mensaje = ""; // Variable para almacenar el mensaje a imprimir + + // Determinar el mensaje basado en el tipo y código + if (type == 3) { // Destination Unreachable + mensaje = "-> Destination Unreachable: "; + if (code == 0) mensaje = "-> Destination Unreachable: Net Unreachable"; + else if (code == 1) mensaje = "-> Destination Unreachable: Host Unreachable"; + else if (code == 2) mensaje = "-> Destination Unreachable: Protocol Unreachable"; + else if (code == 3) mensaje = "-> Destination Unreachable: Port Unreachable"; + else mensaje = "-> Destination Unreachable: Unknown Code"; + } else if (type == 11) { // Time Exceeded + mensaje = "-> Time Exceeded"; + } else { // Otros tipos de error ICMP + mensaje = "-> ICMP Error: "; + } + + // Imprimir el mensaje final + printf("%s (Type %d, Code %d)\n", mensaje, type, code); +} +