Skip to content
Snippets Groups Projects
Select Git revision
  • master
1 result

stduse.h

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    icmp-timestamp.c 6.47 KiB
    /**
     * 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\n");  // Los campos Receive y Transmit no están en icmp_msg
            printf("-> Transmit: 0\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
        socklen_t addr_len = sizeof(dest_addr);
        if (recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest_addr, &addr_len) < 0) {
            perror("Error al recibir el datagrama");
            exit(EXIT_FAILURE);
        }
    
        // Excluir la cabecera IP
        struct iphdr *ip_hdr = (struct iphdr *)buffer;
        int ip_header_length = ip_hdr->ihl * 4;
        TimeStamp *recv_reply = (TimeStamp *)(buffer + ip_header_length);
    
        // Verificar tipo de respuesta
        if (recv_reply->icmpHdr.type == ICMP_TIMESTAMP_REPLY && recv_reply->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->originate);
                unsigned long receive = ntohl(recv_reply->receive);
                unsigned long transmit = ntohl(recv_reply->transmit);
    
                struct timeval end_time;
                gettimeofday(&end_time, NULL);
                unsigned long t4 = (end_time.tv_sec * 1000) + (end_time.tv_usec / 1000);
                unsigned long rtt = (receive - originate) + (t4 - transmit);
    
                if (verbose) {
                    printf("-> Originate: %lu\n", originate);
                    printf("-> Receive: %lu\n", receive);
                    printf("-> Transmit: %lu\n", transmit);
                    printf("-> RTT: %lu seconds\n", rtt/1000);
                    printf("-> TTL: %d\n", ip_hdr->ttl);
                    printf("-> Tamanyo del Datagrama: %ld bytes\n", sizeof(TimeStamp));
                }
    
                printf("-> Respuesta Correcta (Type %d, Code %d)\n", recv_reply->icmpHdr.type, recv_reply->icmpHdr.code);
            }
        } else {
            // Caso de error genérico o inesperado
            imprimeError(recv_reply->icmpHdr.type, recv_reply->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);
    }