Logo Studenta

arp

Esta es una vista previa del archivo. Inicie sesión para ver el archivo original

/***************************************************************************
 * arp.c 
 *
 * Funciones para implementar ARP en las practicas de Arquitectura 
 * Redes de Comunicaciones 2 de la EPS-UAM
 *
 * Autores: Manuel Freire, Jose Hernandez, Javier Ramos
 *
 * (C) 2006-12 EPS-UAM 
 ***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include "nivel1.h"
#include "arp.h"
#include "rc_funcs.h"
// DEFINEs Y ENUMs INTERNOS
#define ARP_DATA_MIN 28
#define ARP_HLEN 6
#define ARP_TLEN 2
#define ARP_REQ_TIMEOUT_US (1000*500)
#define ARP_REQ_RETRIES 3
enum {
 trama_arp_o_eth = 8,
 trama_arp_o_ip = 14,
 trama_arp_d_eth = 18,
 trama_arp_d_ip = 24
};
// VARIABLES GLOBALES INTERNAS
// dir ip local; exportada en 'ip.h', inicializada en arp_inicializa
BYTE dir_ip_local[IP_ALEN];
// dir ethernet local, inicializada en arp_inicializa
BYTE dir_eth_local[ETH_ALEN];
// dir ethernet para broadcast
BYTE dir_eth_any[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// ethertype para tramas ARP
BYTE ethertype_arp[ETH_TLEN] = {0x08, 0x06};
// parte invariante de una trama ARP (IP sobre Ethernet)
BYTE cabecera_arp[ARP_HLEN] = {0, 1, 8, 0, 6, 4};
// tipo de mensaje ARP para solicitud (REQ)
BYTE arptype_req[ARP_TLEN] = {0x00, 0x01};
// tipo de mensaje ARP para respuesta (REP)
BYTE arptype_rep[ARP_TLEN] = {0x00, 0x02};
// si 1, ya se ha inicializado este nivel (y la dir. IP no estaba tomada)
int arp_inicializado = 0;
int ip_validada; // 1 = nadie ha respondido a un gratitious ARP
// entrada de cache ARP
typedef struct {
 time_t expiry; // cuando llegue este momento, habra expirado
 BYTE dir_ip[IP_ALEN]; 
 BYTE dir_eth[ETH_ALEN]; 
} entrada_arp;
// cache ARP (global); todos los accesos deben estar protegidos mediante sem_tabla_arp
struct {
 entrada_arp t[ARP_CACHE_SIZE];
 int n;
} tabla_arp;
// ojo: no se puede incluir dentro de una estructura, porque es mas bien feo...
pthread_mutex_t sem_tabla_arp = PTHREAD_MUTEX_INITIALIZER;
// usada por arp_solicita_direccion_eth / arp_procesa_trama para peticiones en curso: 
// - el que pide pone esperando_respuesta a 1
// - el que recibe lo lee, mete la respuesta en eth_pedida, deja esperando_respuesta a 0
// - el que pide encuentra que esperando_respuesta esta a 0, devuelve eth_pedida
int esperando_respuesta_arp; // 1 = esperando una respuesta
BYTE dir_eth_respuesta_arp[ETH_ALEN];
// PROTOTIPOS INTERNOS
// gestion de cache
void arp_limpia_cache();
int arp_busca_entrada_cache(BYTE *dir_ip, BYTE *dir_ether_buscada);
// NOTA: arp_actualiza_cache es publica: se llama desde IP...
// manejo de tramas ARP
int arp_lee_datos_trama(BYTE *datos_trama, BYTE *tipo, 
 BYTE *dir_eth_origen, BYTE *dir_ip_origen, 
 BYTE *dir_eth_destino, BYTE *dir_ip_destino);
void arp_muestra_datos_trama(BYTE *datos_trama);
void arp_escribe_trama(BYTE *trama, BYTE *tipo, 
 BYTE *dir_eth_destino, BYTE *dir_ip_destino);
int arp_solicita_direccion_eth(BYTE *dir_ip, BYTE *dir_eth_pedida);
// FUNCIONES DE GESTION DE CACHE
/****************************************************************************
 * Limpia correspondencias viejas
 * Basta con buscar la primera entrada no expirada (primeras = mas antiguas),
 * y eliminar todas las anteriores.
 *
 * entra/sale: nada
 ****************************************************************************/
void arp_limpia_cache() {
 int i;
 time_t t = time(NULL);
 
 pthread_mutex_lock(&sem_tabla_arp); 
 { 
 for (i=0; i<tabla_arp.n; i++) {
 if (t < tabla_arp.t[i].expiry) break;
 }
 
 if (i > 0) {
 memmove(tabla_arp.t, tabla_arp.t+i, 
 (tabla_arp.n - i) * sizeof(entrada_arp));
 }
 
 tabla_arp.n -= i;
 } 
 pthread_mutex_unlock(&sem_tabla_arp); 
}
/****************************************************************************
 * Actualiza la cache ARP con una nueva entrada. 
 * Si la entrada ya estaba presente, solo actualiza su 'tiempo de vida'
 * Si no tiene espacio para una nueva entrada, borra la entrada mas antigua.
 *
 * entra:
 * dir_ip, dir_ether - direcciones a introducir en la nueva entrada
 * sale: nada
 ****************************************************************************/
void arp_actualiza_cache(BYTE *dir_ip, BYTE *dir_ether) {
 int i;
 
 arp_limpia_cache();
 
 pthread_mutex_lock(&sem_tabla_arp); 
 {
 // elimina entrada ya existente (si la hay)
 for (i=0; i<tabla_arp.n; i++) {
 if (memcmp(tabla_arp.t[i].dir_ip, dir_ip, IP_ALEN) == 0) { 
 memmove(tabla_arp.t+i, tabla_arp.t+i+1, 
 (tabla_arp.n - i - 1) * sizeof(entrada_arp)); 
 tabla_arp.n --;
 break;
 }
 }
 
 // necesaria entrada nueva; asegurar espacio
 if (tabla_arp.n == ARP_CACHE_SIZE) {
 memmove(tabla_arp.t, tabla_arp.t+1, 
 (tabla_arp.n - 1) * sizeof(entrada_arp)); 
 tabla_arp.n --;
 }
 
 memcpy(tabla_arp.t[tabla_arp.n].dir_ip, dir_ip, IP_ALEN);
 memcpy(tabla_arp.t[tabla_arp.n].dir_eth, dir_ether, ETH_ALEN);
 tabla_arp.t[tabla_arp.n].expiry = time(NULL) + ARP_CACHE_TTL;
 tabla_arp.n ++;
 } 
 pthread_mutex_unlock(&sem_tabla_arp); 
}
/****************************************************************************
 * Busca una entrada la cache ARP con una nueva entrada. 
 * Si la entrada ya estaba presente, solo actualiza su 'tiempo de vida'
 * Si no tiene espacio para una nueva entrada, borra la entrada mas antigua.
 *
 * entra:
 * dir_ip - direccion IP cuya correspondiente dir. ethernet se busca
 * dir_ether_buscada - direccion ethernet donde escribir el resultado
 * sale:
 * 0 si encontrada, -1 si no encontrada
 ****************************************************************************/
int arp_busca_entrada_cache(BYTE *dir_ip, BYTE *dir_ether_buscada) {
 int i, rc;
 
 arp_limpia_cache();
 
 pthread_mutex_lock(&sem_tabla_arp); 
 {
 rc = -1;
 for (i=0; i<tabla_arp.n; i++) {
 if (memcmp(tabla_arp.t[i].dir_ip, dir_ip, IP_ALEN) == 0) {
 memcpy(dir_ether_buscada, tabla_arp.t[i].dir_eth, ETH_ALEN);
 rc = 0;
 break;
 }
 }
 }
 pthread_mutex_unlock(&sem_tabla_arp);
 return rc;
}
/****************************************************************************
 * Muestra la cache ARP por stdout
 * entra / sale: nada
 ****************************************************************************/
void arp_muestra_cache() {
 int i;
 time_t t = time(NULL);
 
 pthread_mutex_lock(&sem_tabla_arp);
 {
 for (i=0; i<tabla_arp.n; i++) {
 // ignora las entradas que ya han expirado, pero no las limpia todavia
 if (t > tabla_arp.t[i].expiry) continue;
 
 printf("%3.3d.%3.3d.%3.3d.%3.3d %2.2x.%2.2x.%2.2x.%2.2x.%2.2x.%2.2x %d\n",
 tabla_arp.t[i].dir_ip[0], tabla_arp.t[i].dir_ip[1], 
 tabla_arp.t[i].dir_ip[2], tabla_arp.t[i].dir_ip[3], 
 tabla_arp.t[i].dir_eth[0], tabla_arp.t[i].dir_eth[1], tabla_arp.t[i].dir_eth[2],
 tabla_arp.t[i].dir_eth[3], tabla_arp.t[i].dir_eth[4], tabla_arp.t[i].dir_eth[5],
 (int)(tabla_arp.t[i].expiry - t));
 }
 } 
 pthread_mutex_unlock(&sem_tabla_arp);
}
// FUNCIONES DE GESTION DE TRAMAS ARP
/****************************************************************************
 * Muestra el contenido de una trama ARP por stdout
 * Si la cabecera ARP es correcta,
 * Muestra las direcciones origen y destino, y el tipo de trama (REQ o REP)
 * Si la cabecera no es correcta,
 * Muestra los bytes de la cabecera.
 *
 * entra:
 * datos_trama - puntero
al comienzo de los datos ARP de una trama Ethernet
 * sale:
 * nada
 ****************************************************************************/
void arp_muestra_datos_trama(BYTE *datos_trama) {
 BYTE dir_eth_origen[ETH_ALEN], dir_ip_origen[IP_ALEN];
 BYTE dir_eth_destino[ETH_ALEN], dir_ip_destino[IP_ALEN];
 BYTE tipo[ARP_TLEN];
 BYTE *ip, *eth;
 int i;
 
 if (arp_lee_datos_trama(datos_trama, tipo, 
 dir_eth_origen, dir_ip_origen, dir_eth_destino, dir_ip_destino) != -1) {
 
 ip = dir_ip_origen;
 eth = dir_eth_origen;
 printf("%3.3d.%3.3d.%3.3d.%3.3d %2.2x.%2.2x.%2.2x.%2.2x.%2.2x.%2.2x -> ",
 ip[0], ip[1], ip[2], ip[3], 
 eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
 ip = dir_ip_destino;
 eth = dir_eth_destino;
 printf("%3.3d.%3.3d.%3.3d.%3.3d %2.2x.%2.2x.%2.2x.%2.2x.%2.2x.%2.2x %s\n",
 ip[0], ip[1], ip[2], ip[3], 
 eth[0], eth[1], eth[2], eth[3], eth[4], eth[5], 
 (memcmp(tipo, arptype_req, ARP_TLEN)==0 ? "?" : "!"));
 }
 else {
 printf("Trama fea: ");
 for (i=0; i<ARP_HLEN; i++) {
 printf("%2.2x ", datos_trama[i]);
 }
 printf("\n");
 } 
}
/****************************************************************************
 * Lee los datos de una trama ARP
 *
 * entra:
 * datos_trama - puntero al comienzo de los datos ARP de una trama Ethernet
 * tipo, dir_eth_origen, dir_ip_origen, dir_eth_destino, dir_ip_destino - 
 * punteros a los campos a rellenar con los datos correpondientes
 * sale:
 * 0 si todo bien, -1 si error (no son datos ARP validos)
 ****************************************************************************/
int arp_lee_datos_trama(BYTE *datos_trama, BYTE *tipo, 
 BYTE *dir_eth_origen, BYTE *dir_ip_origen, 
 BYTE *dir_eth_destino, BYTE *dir_ip_destino)
{
 /* PRACTICA: implementa la funcion; y devuelve 0 si la trama es valida*/
 printf("%s/%d: Falta por implementar 'arp_lee_datos_trama'.\n", __FILE__, __LINE__);
 return -1;
}
/****************************************************************************
 * Prepara una trama ARP para ser enviada, con los datos que se soliciten
 *
 * entra:
 * trama - trama Ethernet a preparar (la parte ARP empieza en trama+ETH_HLEN)
 * tipo - tipo de peticion
 * dir_eth_destino, dir_ip_destino - dir. a incluir en los campos de destino
 * sale:
 * nada
 ****************************************************************************/
void arp_escribe_trama(BYTE *trama, BYTE *tipo, 
 BYTE *dir_eth_destino, BYTE *dir_ip_destino) {
 /* PRACTICA: implementa la funcion */
 printf("%s/%d: Falta por implementar 'arp_escribe_trama'.\n", __FILE__, __LINE__);
}
// FUNCIONES PRINCIPALES DE ARP
/****************************************************************************
 * Procesa una trama Ethernet de tipo ARP
 * Si la direccion 'ip' del campo 'destino' es la propia,
 * Si se trata de una consulta, envia una respuesta
 * Si se trata de una respuesta, y el origen era la ip de una peticion 
 * en curso, considera que ha satisfecho la peticion.
 * En cualquier caso, actualiza la cache con la ip/eth origen de la trama
 *
 * entra:
 * tamano, trama - longitud y datos de la trama ethernet recibida
 * dir_eth_nivel1 - direccion ethernet origen de la trama recibida
 * sale:
 * 0 si no hay fallos, -1 en caso contrario
 ****************************************************************************/
int arp_procesa_trama(int tamano, BYTE *trama, BYTE *dir_eth_nivel1) {
 /* PRACTICA: implementa la parte correspondiente de ARP, teniendo en cuenta */
 /* ... notificar error si la dir_eth_nivel1 no corresponde a la dir. eth. origen */
 /* ... ignorar peticiones cuya dir. eth. origen somos nosotros (ARP gratuito) */
 /* ... usar arp_escribe_trama para responder a peticiones dirigidas a esta IP */
 /* (elimina tambien el siguiente "printf") */
 printf("%s/%d: Falta por implementar 'arp_procesa_trama'.\n", __FILE__, __LINE__);
 
 return 0;
}
/****************************************************************************
 * Solicita la direccion Ethernet que corresponde a una direccion IP.
 * Si la direccion pedida ya esta en la cache, la devuelve sin mas.
 * En caso contrario, la solicita enviando una trama REQ, y reintentando
 * varias veces antes de desistir.
 *
 * entra:
 * dir_ip - direccion IP
 * dir_ether_pedida - direccion Ethernet a rellenar
 * sale:
 * 0 y dir_ether_pedida a su valor correspondiente, o -1 si error
 ****************************************************************************/
int arp_solicita_direccion(BYTE *dir_ip, BYTE *dir_eth_pedida) {
 
 // comprueba si estan pidiendo la dir. propia
 if (memcmp(dir_ip, dir_ip_local, IP_ALEN) == 0) {
 memcpy(dir_eth_pedida, dir_eth_local, ETH_ALEN);
 return 0;
 }
 
 // busca en la cache
 if (arp_busca_entrada_cache(dir_ip, dir_eth_pedida) == 0) {
 // encontrada en cache
 return 0;
 }
 
 // busca en la red mediante ARP
 return arp_solicita_direccion_eth(dir_ip, dir_eth_pedida);
}
 
/****************************************************************************
 * Solicita una direccion ARP directamente al exterior; no usa cache. Esto
 * es util para implementar ARP gratuito, por ejemplo.
 *
 * entra:
 * dir_ip - direccion IP
 * dir_ether_pedida - direccion Ethernet a rellenar
 * sale:
 * 0 y dir_ether_pedida a su valor correspondiente, o -1 si error 
 ****************************************************************************/
int arp_solicita_direccion_eth(BYTE *dir_ip, BYTE *dir_eth_pedida) { 
 /* PRACTICA: implementa esta funcion, con ARP_REQ_RETRIES cada ARP_REQ_TIMEOUT_US */
 /* (usa 'usleep' en lugar de 'sleep') antes de devolver error. */
 /* (y elimina tambien el siguiente "printf") */
 
 printf("%s/%d: 'arp_solicita_direccion_eth' no implementada...\n", __FILE__, __LINE__);
 return -1;
}
/****************************************************************************
 * Inicializa ARP; debe llamarse antes de usar otras funciones de 'arp.h'
 * Se puede llamar repetidas veces sin efectos adversos
 *
 * Lee la variable de entorno 'IPLOCAL' para determinar la IP local.
 *
 * entra: nada
 * sale:
 * 0 si todo bien, -1 en caso de error
 ****************************************************************************/
int arp_inicializa() {
 if ( ! arp_inicializado) {
 
 // obtiene dir. ethernet local
 if (ObtenerDirMAC(dir_eth_local) != ETH_OK) {
 fprintf(stderr, "Error obteniendo dir MAC local\n");
 return -1;
 } 
 // obtiene ip local
 if (lee_cadena_ip(getenv("IPLOCAL"), dir_ip_local) != 0) {
 printf("Error obteniendo dir IP local\n");
 return -1;
 }
 
 arp_inicializado = 1;
 /* PRACTICA: implementar aqui ARP gratuito */
 /* ... y si alguien responde, es que tiene nuestra IP: devolver error */
 /* (elimina tambien el siguiente "printf") */
 printf("%s/%d: Falta por hacer un ARP gratuito...\n", __FILE__, __LINE__);
 
 fprintf(stderr, "Inicializado: %3.3d.%3.3d.%3.3d.%3.3d / %2.2x.%2.2x.%2.2x.%2.2x.%2.2x.%2.2x\n",
 dir_ip_local[0], dir_ip_local[1], dir_ip_local[2], dir_ip_local[3], 
 dir_eth_local[0], dir_eth_local[1], dir_eth_local[2], 
 dir_eth_local[3], dir_eth_local[4], dir_eth_local[5]); 
 }
 
 return 0;
}

Otros materiales