=============================== Sistemas Distribuidos I (75.74) =============================== ------------------------------------------------------------------ TP 2: Sistema de resolución de nombres sobre IP adaptado a sockets ------------------------------------------------------------------ :Author: Leandro Lucarella (77891) Organización ============ En el directorio `src` se encuentra el código fuente del trabajo, con su correspondiente `Makefile` para compilarlo tan solo ejecutando `make`. En el directorio `rutas_ejemplo` contiene algunos archivos con descripciones de rutas de ejemplo para correr los programas. En el directorio `zonas_ejemplo` se encuentran archivos de configuración de zonas de dominios de nombre para realizar pruebas. Uso === El trabajo consta de dos programas, uno llamado `ip` y otro `dns` (más algunas otras pruebas que no tienen relevancia). ip -- El programa `ip` corre 3 procesos (fork(2)eados), uno que recibe paquetes IP otro que recibe entrada del usuario y envía paquetes IP y otro que redirecciona (forward) paquetes IP en caso de ser pertinente. Uso:: ./ip ip [router [forward [route_file [queue_id [proto]]]]] ip IP que utiliza este proceso router 0 si es router, 1 si no lo es (default 0) forward 0 si puede hacer forwarding, 1 si no (default 0) route_file Archivo con las rutas. El formato del archivo es una ruta por línea, cada línea se compone de red (por ahora sólo soporta IPs puntuales), gateway (si es cero es que están en la misma red), MTU y métrica (todavía no se usa), separados por uno o más espacios o tabs (default `route.txt`) queue_id Identificador de la cola a usar como medio físico, también establece el identificador de la cola a usar para comunicarse con el otro proceso (`test_ipout`) para hacer forwarding, que será queue_id + 1 (default `DEV_DEFAULT_KEY` obtenido de `dev.h`) proto Protocolo que transporta (default 0) El programa se queda esperando la entrada del usuario, y sale cuando esta se termina (Ctrl-D). El formato de entrada es:: IP DESTINO MENSAJE Es decir, en una línea se pone la IP de destino y en la línea siguiente el mensaje. Para enviar otro mensaje, nuevamente se pone IP de destino en una línea y el mensaje en la siguiente. dns --- El programa `dns` corre también 3 procesos (fork(2)eados), uno que recibe peticiones de nombres, otro que recibe entrada del usuario y envía peticiones (o mejor dicho encola peticiones para ser enviadas) y otro que realiza el envío de las peticiones realmente. Uso:: ./dns ip [route_file [zone_file [port]]] ip IP que utiliza este proceso (ídem programa `ip`) route_file Archivo con las rutas (ídem programa `ip`) zone_file Archivo con la descripción de las zonas. El formato es muy simple. Cada zona está separada por un renglón en blanco y empieza con una línea con 3 campos separados por espacios o tab: nombre de la zona, TTL y nodo padre. Luego le sigue la lista de registros de esa zona, también con 3 campos por renglón: nombre, tipo de registro (A para indicar una IP, NS para indicar donde buscar registros de una zona con ese nombre) e IP (ya sea la IP definitiva si es A o la IP del nameserver al cual recurrir si es NS). Se pueden ver ejemplos de estos archivos en el disco entregado. port Puerto en el cual escuchará la abstracción de capa física sobre TCP. El programa es muy similar a `ip`, se queda esperando la entrada del usuario, y sale cuando esta se termina (Ctrl-D). El formato de entrada es:: HOSTNAME_A_BUSCAR Es decir, se escribe en una línea el nombre del host del cual se quiera obtener la IP y se presiona ENTER. Diseño del trabajo ================== El trabajo fue desarrollado en C++, orientado a objetos. Se compone de las siguientes clases: Dev Encapsula la capa física y el dispositivo de red. Es una interfaz abstracta. DevQue Implementación de Dev utilizando una cola como medio físico y el id representaría el cable (si 2 dispositivos tienen cola con id distinto serían como si no compartieran el mismo cable). Por simplicidad a la cola siempre se envía el tamaño del MTU completo pero se agrega una cabecera con el tamaño real del frame. DevTCP Implementación de Dev utilizando conexiones TCP. Por cada frame saliente a un destino en particular se crea una conexión TCP (a menos que ya esté creada, en cuyo caso se reutiliza) y se escucha por conexiones entrantes para recibir frames (también guardándolas para reutilizarlas). A diferencia del DevQue se envía el tamaño del frame exacto (en realidad se agrega una pequeña cabecera). IPAddr Clase auxiliar que encapsula una dirección IP. IPHeader Encapsula una cabecera IP. El cálculo de checksum se simplificó (haciendo una suma byte a byte de toda la cabecera) porque cumple con el objetivo didactico de todas maneras. RouteTable Encapsula una tabla de ruteo. Por falta de tiempo y simplicidad por ahora sólo soporta rutas a un sólo host (no a una red) pero es muy fácilmente extensible y transparente para el resto de las clases que la usan. Las rutas se componen de red (en realidad por ahora host), geteway, MTU, metrica y dispositivo de red por el cual salir (Dev). IPIn Es la clase encargada de recibir paquetes IP. Hace chequeos varios y descarta paquetes según los siguientes criterios: * Cabecera incompleta o no es IP * Versión IP incorrecta * Mal checksum * TTL=0 * No es para nosotros y no hacemos forward * Es para nosotros pero somos un router Si hace forwarding le pasa a IPOut el paquete por una cola y reensabla de ser necesario. IPOut Es la clase encargada de enviar paquetes IP. Tiene una RouteTable para hacer el ruteo y verifica si hay paquetes a forwardear antes de enviar lo que le piden. También fragmenta y puede "descartar" paquetes según estos criterios: * No existe una ruta para el destino * Tamaño de paquete más grande que MTU y DF=1 libtcp Es el único componente del TP que no es orientado a objetos ya que se reutilizó de trabajos anteriores. Es una pequeña abstracción sobre la API de sockets de BSD para mayor simplicidad. ResolvProtoRequest Clase que encapsula una petición de resolución de nombre a enviar via IP. ResolvProtoResponse Clase que encapsula una respuesta de resolución de nombre a enviar via IP. NameServer Clase encargada de resolver los nombres. Escucha por peticiones y puede resolverlas recursiva o directamente (según venga de otro NameServer o de un Resolver la petición). Esta clase se compone de varias otras clases auxiliares para almacenar las zonas y el cache. Ejemplo de corrida ================== Archivo de configuración de zonas de 10.10.10.2:: homero.casa 600 10.10.10.1 tito A 10.10.100.1 juan A 10.10.100.2 juan A 10.10.100.3 pepe A 10.10.100.4 juan A 10.10.100.5 pepe A 10.10.100.6 marge NS 10.10.10.3 todos NS 10.10.10.1 todos NS 10.10.10.3 todos NS 10.10.10.141 Línea de comandos:: ./dns 10.10.10.2 ../rutas_ejemplo/mi_lan.txt ../zonas_ejemplo/10.10.10.2.txt Resolución de un nombre local con una sola IP --------------------------------------------- Línea de comandos:: ./dns 10.10.10.2 ../rutas_ejemplo/mi_lan.txt ../zonas_ejemplo/10.10.10.2.txt Salida:: tito.homero.casa Resolviendo tito.homero.casa... resolv_direct -> tratando de resolver: tito.homero.casa resolv_direct found (local/hijo): ResolvProtoResponse(ret=2, ttl=600, 10.10.100.1) resolv_recursive -> gotcha! ResolvProtoResponse(ret=2, ttl=600, 10.10.100.1) Resultado: ResolvProtoResponse(ret=2, ttl=600, 10.10.100.1) Resolución de un nombre local con múltiples IP ---------------------------------------------- Salida:: juan.homero.casa Resolviendo juan.homero.casa... resolv_direct -> tratando de resolver: juan.homero.casa resolv_direct found (local/hijo): ResolvProtoResponse(ret=2, ttl=600, 10.10.100.2, 10.10.100.3, 10.10.100.5) resolv_recursive -> gotcha! ResolvProtoResponse(ret=2, ttl=600, 10.10.100.2, 10.10.100.3, 10.10.100.5) Resultado: ResolvProtoResponse(ret=2, ttl=600, 10.10.100.2, 10.10.100.3, 10.10.100.5) Resolución de un nombre local no existente ------------------------------------------ Salida:: none.homero.casa Resolviendo none.homero.casa... resolv_direct -> tratando de resolver: none.homero.casa resolv_direct NOT FOUND (es local pero no existe) Resultado: ResolvProtoResponse(ret=4, ttl=0) Resolución de un nombre remoto con múltiples IP y 2 niveles de indirección -------------------------------------------------------------------------- Archivo de configuración de zonas de 10.10.10.1:: casa 600 0.0.0.0 burns A 10.10.10.1 homero A 10.10.10.2 marge A 10.10.10.3 manuk A 10.10.10.141 juan A 100.10.100.5 pepe A 100.10.100.6 homero NS 10.10.10.2 marge NS 10.10.10.3 manuk NS 10.10.10.141 burns.casa 600 0.0.0.0 tito A 100.10.100.1 juan A 100.10.100.2 juan A 100.10.100.3 pepe A 100.10.100.4 juan A 100.10.100.5 pepe A 100.10.100.6 todos.homero.casa 9500 0.0.0.0 tito A 10.1.100.1 juan A 10.1.100.2 juan A 10.1.100.3 pepe A 10.1.100.4 juan A 10.1.100.5 pepe A 10.1.100.6 Archivo de configuración de zonas de 10.10.10.3:: marge.casa 600 10.10.10.1 tito A 30.10.100.1 juan A 30.10.100.2 juan A 30.10.100.3 pepe A 30.10.100.4 juan A 30.10.100.5 pepe A 30.10.100.6 manuk NS 10.10.10.141 todos NS 10.10.10.1 todos NS 10.10.10.3 todos NS 10.10.10.141 todos.homero.casa 9500 10.10.10.2 tito A 10.1.100.1 juan A 10.1.100.2 juan A 10.1.100.3 pepe A 10.1.100.4 juan A 10.1.100.5 pepe A 10.1.100.6 marge.homero.casa 9500 10.10.10.2 tito A 10.3.100.1 juan A 10.3.100.2 juan A 10.3.100.3 pepe A 10.3.100.4 juan A 10.3.100.5 pepe A 10.3.100.6 La petición se realiza desde 10.10.10.2, quien debe recurir a su nodo padre (10.10.10.1) que indica que el encargado de resolver esa zona es 10.10.10.3. Salida de 10.10.10.2:: pepe.marge.casa Resolviendo pepe.marge.casa... resolv_direct -> tratando de resolver: pepe.marge.casa resolv_direct -> evaluando padre 10.10.10.1 resolv_direct found (al padre): 10.10.10.1 resolv_recursive -> redirect a ResolvProtoResponse(ret=3, ttl=600, 10.10.10.1) query -> pidiendo ResolvProtoRequest(query_type=0, name=pepe.marge.casa) a 10.10.10.1 query -> recibido ResolvProtoResponse(ret=3, ttl=600, 10.10.10.3) de 10.10.10.1 resolv_recursive_r -> redirect a ResolvProtoResponse(ret=3, ttl=600, 10.10.10.3) query -> pidiendo ResolvProtoRequest(query_type=0, name=pepe.marge.casa) a 10.10.10.3 query -> recibido ResolvProtoResponse(ret=2, ttl=600, 30.10.100.4, 30.10.100.6) de 10.10.10.3 resolv_recursive_r -> gotcha! ResolvProtoResponse(ret=2, ttl=600, 30.10.100.4, 30.10.100.6) Resultado: ResolvProtoResponse(ret=2, ttl=600, 30.10.100.4, 30.10.100.6) Salida de 10.10.10.1:: NameServer::send_loop() -> recibido ResolvProtoRequest(query_type=0, name=pepe.marge.casa) resolv_direct -> tratando de resolver: pepe.marge.casa resolv_direct found (local/hijo): ResolvProtoResponse(ret=3, ttl=600, 10.10.10.3) NameServer::send_loop() -> respondo ResolvProtoResponse(ret=3, ttl=600, 10.10.10.3) Salida de 10.10.10.3:: NameServer::send_loop() -> recibido ResolvProtoRequest(query_type=0, name=pepe.marge.casa) resolv_direct -> tratando de resolver: pepe.marge.casa resolv_direct found (local/hijo): ResolvProtoResponse(ret=2, ttl=600, 30.10.100.4, 30.10.100.6) NameServer::send_loop() -> respondo ResolvProtoResponse(ret=2, ttl=600, 30.10.100.4, 30.10.100.6) .. vim: filetype=rst sw=2 sts=2 et :