Raw Sockets en Linux ejemplo de cliente (generador de paquetes)

 

Notas de recordatorio. 

LLDP (capa 2) es para reconocer una topología de red.


Explicación paso a paso: 

1. Creamos un socket RAW con AF_PACKET para enviar paquetes Ethernet directamente.

socketfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));


2. Obtenemos el índice de la interfaz de red para enviarlo correctamente.

En este ejemplo es necesario saber el nombre de la interfaz. 

El kernel usa una lista (index) de interfaces, hay que obtener ese número para luego insertarlo en la estructura que almacena la dirección de destino. La función que hace esto (obtener el index) ya esta definida en <net/if.h>.

unsigned int if_index = if_nametoindex(INTERFACE);


3. Preparamos la estructura sockaddr_ll para definir la interfaz destino.
La estructura para la dirección ya es predefinida, hay que usarla así (no tiene más motivo que eso) 

struct sockaddr_ll dest_address;

Esta estructura predefinida para los sockets BSD tiene varios campos, a nosotros nos interesa llenar dos de ellos (de momento).

El campo donde va el interfaz index que obtuvimos recién:

dest_address.sll_ifindex = if_index;

y otro campo llamado ‘halen’ (hardware lenght) para el largo de la MAC (que siempre es 6)

dest_address.sll_halen = ETH_ALEN;

ETH_ALEN es una constante definida en <net/if_ether.h>. Su valor es 6 porque todas las direcciones MAC tienen 6 bytes.


4. Construimos el paquete Ethernet:

Declaramos MAC destino: FF:FF:FF:FF:FF:FF (broadcast).

unsigned char dest_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

Declaramos MAC origen: 00:11:22:33:44:55 (debes cambiarla según corresponda).

unsigned char src_mac[6] = {0x4C, 0xCC, 0x6A, 0xF9, 0xBC, 0xA7};


Empezamos a copiar la info en el buffer que será nuestro paquete. Lo primero es la MAC de destino.

memcpy(packet, dest_mac, 6);

'packet' es el buffer que usamos para construir el paquete Ethernet. 

6 es el número de bytes a copiar. (Las MAC address siempre son de 6 bytes). Bien podría usarse ETH_ALEN en este campo.


Lo siguiente es la MAC de origen.

memcpy(packet + 6, src_mac, 6);


Luego el EtherType. En este ejemplo IPv4 (0x0800)

packet[12] = 0x08;  // Primer byte del EtherType

packet[13] = 0x00;  // Segundo byte del EtherType


Finalmente la data a transmitir en si misma.

#define ETHERNET_HEADER_SIZE 14   // Tamaño del encabezado Ethernet (6+6+2 bytes)

#define PAYLOAD_SIZE 42           // Tamaño en bytes del mensaje de carga útil (puede modificarse)


// Llenar la carga útil con datos ficticios ('A')

memset(packet + ETHERNET_HEADER_SIZE, 'A', PAYLOAD_SIZE);


*) Con esto ya tenemos construido un paquete que contiene lo básico:

6 bytes con la MAC de destino. 

6 bytes con la MAC de origen 

2 bytes con el EtherType 

El resto con el “payload” (los datos que se quieren transmitir) 


5. Envío del paquete. 

if (sendto (socketfd, packet, ETHERNET_HEADER_SIZE + PAYLOAD_SIZE, 0,
(struct sockaddr*)&dest_address, sizeof(struct sockaddr_ll)) < 0) {

perror("Error sending packet"); // Mensaje de error si la transmisión falla

    close(socketfd);

    return 1;

}



6. Cerramos el socket cuando terminamos.

close(socketfd);