// ipv4_header.c — lee un paquete de la salida de "tcpdump -x" y muestra los
// campos de su encabezado IPv4, byte a byte. Se usa así:
//
//   docker exec clab-w04-fwd-r1 tcpdump -l -n -x -c 1 -i eth3 icmp | ./ipv4_header
//
// Solo mira el primer paquete que recibe.
#define _POSIX_C_SOURCE 200809L
#include <arpa/inet.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#define HEADER_MIN 20
#define MAX_BYTES 65535

static int hex_value(char c) {
    if (c >= '0' && c <= '9') {
        return c - '0';
    }
    if (c >= 'a' && c <= 'f') {
        return c - 'a' + 10;
    }
    if (c >= 'A' && c <= 'F') {
        return c - 'A' + 10;
    }
    return -1;
}

// Agrega a bytes lo que hay después de "0xNNNN:" en una línea de tcpdump -x.
// Devuelve cuántos bytes hay en total; las líneas sin ese inicio no cuentan.
static size_t read_hex_line(const char *line, uint8_t *bytes, size_t count) {
    const char *p = line;
    while (isspace((unsigned char)*p)) {
        p++;
    }
    if (strncmp(p, "0x", 2) != 0) {
        return count;
    }
    p = strchr(p, ':');
    if (p == NULL) {
        return count;
    }
    p++;

    while (*p != '\0' && count < MAX_BYTES) {
        if (isspace((unsigned char)*p)) {
            p++;
            continue;
        }
        int high = hex_value(p[0]);
        int low = high < 0 ? -1 : hex_value(p[1]);
        if (low < 0) {
            break;
        }
        bytes[count++] = (uint8_t)(high * 16 + low);
        p += 2;
    }
    return count;
}

static const char *protocol_name(uint8_t protocol) {
    switch (protocol) {
    case 1:
        return "ICMP";
    case 6:
        return "TCP";
    case 17:
        return "UDP";
    default:
        return "otro";
    }
}

static uint16_t read_u16(const uint8_t *p) {
    return (uint16_t)((p[0] << 8) | p[1]);
}

static void print_address(const char *label, const uint8_t *p) {
    char text[INET_ADDRSTRLEN];
    if (inet_ntop(AF_INET, p, text, sizeof text) == NULL) {
        perror("inet_ntop");
        return;
    }
    printf("%-12s %s\n", label, text);
}

int main(void) {
    static uint8_t bytes[MAX_BYTES];
    size_t count = 0;
    bool started = false;
    char line[256];

    while (fgets(line, sizeof line, stdin) != NULL) {
        size_t before = count;
        count = read_hex_line(line, bytes, count);
        if (count > before) {
            started = true;
        } else if (started) {
            break;  // terminó el primer paquete
        }
    }
    if (ferror(stdin)) {
        perror("stdin");
        return 1;
    }
    if (count < HEADER_MIN) {
        fprintf(stderr, "ipv4_header: se leyeron %zu bytes; faltan datos\n", count);
        return 1;
    }

    unsigned version = bytes[0] >> 4;
    unsigned header_length = (unsigned)(bytes[0] & 0x0f) * 4;
    if (version != 4 || header_length < HEADER_MIN || header_length > count) {
        fprintf(stderr, "ipv4_header: no es un encabezado IPv4 válido\n");
        return 1;
    }

    uint16_t flags_offset = read_u16(&bytes[6]);
    unsigned offset_units = flags_offset & 0x1fff;

    printf("versión      %u\n", version);
    printf("%-12s %u bytes\n", "encabezado", header_length);
    printf("%-12s DSCP %u, ECN %u\n", "ToS", (unsigned)(bytes[1] >> 2), (unsigned)(bytes[1] & 3));
    printf("%-12s %u bytes\n", "largo total", read_u16(&bytes[2]));
    printf("%-12s 0x%04x\n", "id", read_u16(&bytes[4]));
    printf("%-12s DF=%u MF=%u\n", "flags", (flags_offset >> 14) & 1u, (flags_offset >> 13) & 1u);
    printf("%-12s %u (%u bytes)\n", "offset", offset_units, offset_units * 8);
    printf("%-12s %u\n", "TTL", bytes[8]);
    printf("%-12s %u (%s)\n", "protocolo", bytes[9], protocol_name(bytes[9]));
    printf("%-12s 0x%04x\n", "checksum", read_u16(&bytes[10]));
    print_address("origen", &bytes[12]);
    print_address("destino", &bytes[16]);
    return 0;
}
