--- /dev/null
+Enunciado extraoficial
+
+Hay que hacer 2 procesos, uno que manda y otro que recibe IP, por cada
+host/router. Todos los procesos que envian, ponen las cosas en una cola, todos
+los que reciben, sacan de esa cola. Se usa como MAC la IP, y como ID del mensaje
+de la cola (de esta manera cada proceso saca solo los "paquetes" con el ID/MAC
+que le corresponda).
+
+1) Campos en IP
+ id de paquete
+ ip origen
+ ip destino
+ checksum (0/1, de juguete)
+ tamaño del paquete completo
+ ToS
+ Don't Fragment (0/1)
+ End (0/1)
+ offset
+ TTL
+ tamaño de este fragmento
+ tipo de payload (IP / ICMP)
+ (se que faltan algunos, si tienen algo mas,
+ completen)
+
+2) Casos de descarte de paquetes
+ Error de checksum (silencioso)
+ No hay buffer para fragmento (silencioso)
+ Un host que no rutea recibe un paquete para otro host
+ (silencioso)
+ No hay ruta (icmp)
+ DF == 1 y MTU < size (icmp)
+ TTL == 0 (icmp)
+
+3) Comportamiento del protocolo
+ Debe rutear (si es un router)
+ Debe fragmentar y reensamblar
+ Debe contemplar todos los casos de descarte de
+ paquetes anteriores escribiendo en un archivo
+ los paquetes descartados según corresponda
+ (silecioso, icmp).
+
--- /dev/null
+===============================
+Sistemas Distribuidos I (75.74)
+===============================
+
+----------------------------
+Trabajo práctico de stack IP
+----------------------------
+
+: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`.
+Dentro de este directorio hay también dos scripts de pruebas completas:
+`test.sh` (con 1 router) y `test2.sh` (con 2 routers).
+
+En el directorio `rutas_ejemplo` contiene algunos archivos con descripciones de
+rutas de ejemplo para correr los programas.
+
+
+Uso
+===
+
+El trabajo consta de un programa llamado `ip` (más algunas otras pruebas que no
+tiene relevancia). Este programa corre 3 procesos, 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 está 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.
+
+
+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. Utiliza 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.
+
+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
+
+
+Ejemplo de corrida
+==================
+
+Host 10.10.10.1
+---------------
+
+Rutas:
+
+* 10.10.10.1 0.0.0.0 35 0
+* 10.10.10.3 10.10.10.5 35 1
+* 10.10.10.5 0.0.0.0 35 0
+
+Envía "adios mundo cruel!!!" al host 10.10.10.3::
+
+ $ (echo -e '10.10.10.3\nAdios mundo cruel!!!'; sleep 1) | ./ip 10.10.10.1 0 0 ../rutas_ejemplo/route_10.10.10.1.txt
+ IPOut::send (10.10.10.1): Fragmento 0 => IPHeader: version=4 total_len=40 id=44919 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=214 src=10.10.10.1 dst=10.10.10.3
+ data (15) = Adios mundo cru
+ IPOut::send (10.10.10.1): Fragmento 1 => IPHeader: version=4 total_len=25 id=44919 DF=0 MF=0 offset=15 TTL=64 proto=0 checksum=315 src=10.10.10.1 dst=10.10.10.3
+ data (5) = el!!!
+ Enviado 'Adios mundo cruel!!!' a 10.10.10.3
+
+
+Router 10.10.10.5
+-----------------
+
+Rutas:
+
+* 10.10.10.1 0.0.0.0 35 0
+* 10.10.10.3 0.0.0.0 32 0
+* 10.10.10.5 0.0.0.0 35 0
+
+Recibe el mensaje::
+
+ $ ./ip 10.10.10.5 1 1 ../rutas_ejemplo/route_10.10.10.5.txt
+ IPIn::recv (10.10.10.5): IPHeader: version=4 total_len=40 id=44919 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=214 src=10.10.10.1 dst=10.10.10.3
+ data (15) = Adios mundo cru
+ IPIn::recv (10.10.10.5): IPHeader: version=4 total_len=25 id=44919 DF=0 MF=0 offset=15 TTL=64 proto=0 checksum=315 src=10.10.10.1 dst=10.10.10.3
+ data (5) = el!!!
+ IPOut::forward_loop (10.10.10.5): A forwardear (id 44919)
+ IPOut::send (10.10.10.5): Fragmento 0 => IPHeader: version=4 total_len=40 id=44919 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=214 src=10.10.10.1 dst=10.10.10.3
+ data (12) = Adios mundo
+ IPOut::send (10.10.10.5): Fragmento 1 => IPHeader: version=4 total_len=28 id=44919 DF=0 MF=1 offset=12 TTL=64 proto=0 checksum=298 src=10.10.10.1 dst=10.10.10.3
+ data (3) = cru
+ IPOut::forward_loop (10.10.10.5): A forwardear (id 44919)
+ IPOut::send (10.10.10.5): Fragmento 0 => IPHeader: version=4 total_len=25 id=44919 DF=0 MF=0 offset=15 TTL=64 proto=0 checksum=315 src=10.10.10.1 dst=10.10.10.3
+ data (5) = el!!!
+
+
+Host 10.10.10.3
+---------------
+
+Rutas:
+
+* 10.10.10.1 10.10.10.5 32 1
+* 10.10.10.3 0.0.0.0 32 0
+* 10.10.10.5 0.0.0.0 32 0
+
+Finalmente este host recibe todos los fragmentos, reensabla y pasa el
+ paquete completo a la capa superior::
+
+ $ ./ip 10.10.10.3 0 0 ../rutas_ejemplo/route_10.10.10.3.txt
+ IPIn::recv (10.10.10.3): IPHeader: version=4 total_len=40 id=44919 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=214 src=10.10.10.1 dst=10.10.10.3
+ data (12) = Adios mundo
+ IPIn::recv (10.10.10.3): IPHeader: version=4 total_len=28 id=44919 DF=0 MF=1 offset=12 TTL=64 proto=0 checksum=298 src=10.10.10.1 dst=10.10.10.3
+ data (3) = cru
+ IPIn::recv (10.10.10.3): IPHeader: version=4 total_len=25 id=44919 DF=0 MF=0 offset=15 TTL=64 proto=0 checksum=315 src=10.10.10.1 dst=10.10.10.3
+ data (5) = el!!!
+ IPIn::recv (10.10.10.3): Paquete completo: data = 'Adios mundo cruel!!!'
+ Recibido 'Adios mundo cruel!!!' (len 20) de 10.10.10.1 para 10.10.10.3 (proto = 0)
+
+.. vim: filetype=rst :
--- /dev/null
+- Implementar metricas
+- Implementar rutas de redes completas
+- Arreglar cola de forwarding para que no dependa del medio fisico/dispositivo
+- Tener en cuenta el TTL para limpiar buffers
+- Ver que hayan llegado todos los fragmentos antes de subir a capa superior
+- Separar descartes de ICMP de silenciosos
--- /dev/null
+10.10.10.1 0.0.0.0 25 0
+10.10.10.2 0.0.0.0 25 0
+10.10.10.3 10.10.10.5 25 0
--- /dev/null
+10.10.10.1 0.0.0.0 35 0
+10.10.10.2 10.10.10.5 35 2
+10.10.10.3 10.10.10.5 35 1
+10.10.10.4 10.10.10.5 35 1
+10.10.10.5 0.0.0.0 35 0
--- /dev/null
+10.10.10.1 10.10.10.4 25 2
+10.10.10.2 0.0.0.0 25 0
+10.10.10.3 10.10.10.4 25 2
+10.10.10.4 0.0.0.0 25 0
+10.10.10.5 10.10.10.4 25 1
--- /dev/null
+10.10.10.1 10.10.10.5 32 1
+10.10.10.2 10.10.10.5 32 2
+10.10.10.3 0.0.0.0 32 0
+10.10.10.4 10.10.10.5 32 1
+10.10.10.5 0.0.0.0 32 0
--- /dev/null
+10.10.10.1 10.10.10.5 28 1
+10.10.10.2 0.0.0.0 25 0
+10.10.10.3 10.10.10.5 28 1
+10.10.10.4 0.0.0.0 28 0
+10.10.10.5 0.0.0.0 28 0
--- /dev/null
+10.10.10.1 0.0.0.0 35 0
+10.10.10.2 10.10.10.4 28 1
+10.10.10.3 0.0.0.0 32 0
+10.10.10.4 0.0.0.0 28 0
+10.10.10.5 0.0.0.0 35 0
--- /dev/null
+# Makefile de ejemplo para C++
+#
+# Creado: jue abr 15 15:34:19 ART 2004
+#
+# Copyleft 2004 - Leandro Lucarella, Bajo licencia GPL [http://www.gnu.org/]
+#
+
+# CONFIGURACION
+################
+
+# Opciones para el compilador C/C++ en modo ansi.
+CFLAGS = -Wall -ansi -pedantic-errors
+
+# Para que explote lo mas posible
+#CFLAGS += -O3 -DNDEBUG
+
+# Para valgrind o debug
+CFLAGS += -ggdb -DDEBUG
+
+# Opciones para el compilador C++.
+CXXFLAGS = $(CFLAGS) -fno-inline
+
+# Opciones del enlazador.
+#LDFLAGS=
+
+# Compilador.
+CC=g++
+
+# Programas
+targets=ip
+tests=test_send test_recv test_ipaddr test_ipin test_ipout
+
+# Fuentes
+fuentes ?= $(wildcard *.cpp)
+
+
+# REGLAS
+#########
+
+.PHONY: all clean
+
+all: depend $(targets)
+
+tests: depend $(tests)
+
+test_send: test_send.o dev.o
+
+test_recv: test_recv.o dev.o
+
+test_ipaddr: test_ipaddr.o ipaddr.o ipheader.o
+
+test_ipin: test_ipin.o ipin.o ipaddr.o ipheader.o dev.o
+
+test_ipout: test_ipout.o ipout.o ipaddr.o ipheader.o dev.o routetable.o
+
+ip: ip.o ipout.o ipin.o ipaddr.o ipheader.o dev.o routetable.o
+
+depend:
+ @makedepend $(fuentes) > /dev/null 2>&1
+
+clean:
+ @$(RM) -fv *.o Makefile.bak $(targets)
+
+# DO NOT DELETE
+
+dev.o: dev.h /usr/include/unistd.h /usr/include/features.h
+dev.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+dev.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+dev.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h
+dev.o: /usr/include/bits/confname.h /usr/include/getopt.h
+dev.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+dev.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h
+dev.o: /usr/include/bits/endian.h /usr/include/sys/select.h
+dev.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+dev.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h
+dev.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+dev.o: /usr/include/sys/ipc.h /usr/include/bits/ipctypes.h
+dev.o: /usr/include/bits/ipc.h /usr/include/sys/msg.h /usr/include/bits/msq.h
+ip.o: ipout.h ipaddr.h ipheader.h /usr/include/stdint.h
+ip.o: /usr/include/features.h /usr/include/sys/cdefs.h
+ip.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+ip.o: /usr/include/bits/wordsize.h routetable.h dev.h ipin.h
+ip.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+ip.o: /usr/include/bits/types.h /usr/include/bits/typesizes.h
+ip.o: /usr/include/bits/confname.h /usr/include/getopt.h /usr/include/fcntl.h
+ip.o: /usr/include/bits/fcntl.h /usr/include/sys/types.h /usr/include/time.h
+ip.o: /usr/include/endian.h /usr/include/bits/endian.h
+ip.o: /usr/include/sys/select.h /usr/include/bits/select.h
+ip.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+ip.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+ip.o: /usr/include/bits/sched.h /usr/include/sys/wait.h /usr/include/signal.h
+ip.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h
+ip.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h
+ip.o: /usr/include/asm/sigcontext.h /usr/include/asm-i486/sigcontext.h
+ip.o: /usr/include/linux/compiler.h /usr/include/bits/sigstack.h
+ip.o: /usr/include/bits/sigthread.h /usr/include/sys/resource.h
+ip.o: /usr/include/bits/resource.h /usr/include/bits/waitflags.h
+ip.o: /usr/include/bits/waitstatus.h /usr/include/sys/ipc.h
+ip.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+ip.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
+ipaddr.o: ipaddr.h
+ipheader.o: ipheader.h ipaddr.h /usr/include/stdint.h /usr/include/features.h
+ipheader.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+ipheader.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+ipin.o: ipin.h ipaddr.h ipheader.h /usr/include/stdint.h
+ipin.o: /usr/include/features.h /usr/include/sys/cdefs.h
+ipin.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+ipin.o: /usr/include/bits/wordsize.h dev.h
+ipout.o: ipout.h ipaddr.h ipheader.h /usr/include/stdint.h
+ipout.o: /usr/include/features.h /usr/include/sys/cdefs.h
+ipout.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+ipout.o: /usr/include/bits/wordsize.h routetable.h dev.h
+ipout.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+ipout.o: /usr/include/bits/types.h /usr/include/bits/typesizes.h
+ipout.o: /usr/include/bits/confname.h /usr/include/getopt.h
+ipout.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+ipout.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h
+ipout.o: /usr/include/bits/endian.h /usr/include/sys/select.h
+ipout.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+ipout.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h
+ipout.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+ipout.o: /usr/include/sys/ipc.h /usr/include/bits/ipctypes.h
+ipout.o: /usr/include/bits/ipc.h /usr/include/sys/msg.h
+ipout.o: /usr/include/bits/msq.h
+routetable.o: routetable.h dev.h ipaddr.h
+test_ipaddr.o: ipaddr.h ipheader.h /usr/include/stdint.h
+test_ipaddr.o: /usr/include/features.h /usr/include/sys/cdefs.h
+test_ipaddr.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+test_ipaddr.o: /usr/include/bits/wordsize.h
+test_ipin.o: ipin.h ipaddr.h ipheader.h /usr/include/stdint.h
+test_ipin.o: /usr/include/features.h /usr/include/sys/cdefs.h
+test_ipin.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+test_ipin.o: /usr/include/bits/wordsize.h dev.h /usr/include/unistd.h
+test_ipin.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+test_ipin.o: /usr/include/bits/typesizes.h /usr/include/bits/confname.h
+test_ipin.o: /usr/include/getopt.h /usr/include/fcntl.h
+test_ipin.o: /usr/include/bits/fcntl.h /usr/include/sys/types.h
+test_ipin.o: /usr/include/time.h /usr/include/endian.h
+test_ipin.o: /usr/include/bits/endian.h /usr/include/sys/select.h
+test_ipin.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+test_ipin.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h
+test_ipin.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+test_ipin.o: /usr/include/sys/ipc.h /usr/include/bits/ipctypes.h
+test_ipin.o: /usr/include/bits/ipc.h /usr/include/sys/msg.h
+test_ipin.o: /usr/include/bits/msq.h
+test_ipout.o: ipout.h ipaddr.h ipheader.h /usr/include/stdint.h
+test_ipout.o: /usr/include/features.h /usr/include/sys/cdefs.h
+test_ipout.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+test_ipout.o: /usr/include/bits/wordsize.h routetable.h dev.h
+test_ipout.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+test_ipout.o: /usr/include/bits/types.h /usr/include/bits/typesizes.h
+test_ipout.o: /usr/include/bits/confname.h /usr/include/getopt.h
+test_ipout.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+test_ipout.o: /usr/include/sys/types.h /usr/include/time.h
+test_ipout.o: /usr/include/endian.h /usr/include/bits/endian.h
+test_ipout.o: /usr/include/sys/select.h /usr/include/bits/select.h
+test_ipout.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+test_ipout.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+test_ipout.o: /usr/include/bits/sched.h /usr/include/sys/ipc.h
+test_ipout.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+test_ipout.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
+test_recv.o: dev.h /usr/include/unistd.h /usr/include/features.h
+test_recv.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+test_recv.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+test_recv.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h
+test_recv.o: /usr/include/bits/confname.h /usr/include/getopt.h
+test_recv.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+test_recv.o: /usr/include/sys/types.h /usr/include/time.h
+test_recv.o: /usr/include/endian.h /usr/include/bits/endian.h
+test_recv.o: /usr/include/sys/select.h /usr/include/bits/select.h
+test_recv.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+test_recv.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+test_recv.o: /usr/include/bits/sched.h /usr/include/sys/ipc.h
+test_recv.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+test_recv.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
+test_send.o: dev.h /usr/include/unistd.h /usr/include/features.h
+test_send.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+test_send.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+test_send.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h
+test_send.o: /usr/include/bits/confname.h /usr/include/getopt.h
+test_send.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+test_send.o: /usr/include/sys/types.h /usr/include/time.h
+test_send.o: /usr/include/endian.h /usr/include/bits/endian.h
+test_send.o: /usr/include/sys/select.h /usr/include/bits/select.h
+test_send.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+test_send.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+test_send.o: /usr/include/bits/sched.h /usr/include/sys/ipc.h
+test_send.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+test_send.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
--- /dev/null
+#include "dev.h"
+#include <cstring>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+struct Frame
+{
+ Dev::mac_type mac;
+ size_t size;
+ char frame[1];
+};
+
+Dev::Dev(mac_type mac, key_t key, size_t mtu)
+ throw (std::runtime_error, std::logic_error):
+ mac(mac), mtu(mtu)
+{
+ if (mtu > DEV_MAX_MTU)
+ throw std::logic_error("MTU más grande que DEV_MAX_MTU");
+ que_id = msgget(key, 0666); // Debe estar previamente creada
+ if (que_id == -1)
+ throw std::runtime_error("No se pudo crear la cola");
+}
+
+void Dev::transmit(const std::string& data, const mac_type& mac)
+ throw (std::runtime_error, std::logic_error)
+{
+ if (data.size() > mtu)
+ throw std::logic_error("Tamaño de datos mayor al MTU");
+ Frame* f = (Frame*) malloc(sizeof(Frame) + mtu);
+ if (!f)
+ throw std::runtime_error("No se puede reservar memoria");
+ f->mac = mac;
+ f->size = data.size();
+ memcpy(f->frame, data.c_str(), data.size());
+ int res = msgsnd(que_id, f, mtu + sizeof(size_t), 0);
+#ifdef DEBUG
+ //std::cout << "Dev::transmit(msgtype/mac = " << f->mac << ", size = "
+ // << f->size << ")\n";
+#endif
+ free(f);
+ if (res == -1)
+ throw std::runtime_error("Error al poner en la cola");
+}
+
+std::string Dev::receive() throw (std::runtime_error)
+{
+ Frame* f = (Frame*) malloc(sizeof(Frame) + mtu);
+ if (!f)
+ throw std::runtime_error("No se puede reservar memoria");
+ int res = msgrcv(que_id, f, mtu + sizeof(size_t), mac, 0);
+ if (res == -1)
+ {
+ free(f);
+ throw std::runtime_error("Error al sacar de la cola");
+ }
+ std::string s((char*) f->frame, f->size);
+ free(f);
+#ifdef DEBUG
+ //std::cout << "Dev::receive(msgtype/mac = " << mac << ", size = "
+ // << s.size() << ")\n";
+#endif
+ return s;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _DEV_H_
+#define _DEV_H_
+
+#include <stdexcept>
+
+#define DEV_DEFAULT_KEY 0x1abcdef1
+#define DEV_MAX_MTU 1500
+
+/// Dispositivo de red (capa de enlace)
+struct Dev
+{
+
+ /// Tipo de la mac
+ typedef long mac_type;
+
+ /// Dirección MAC
+ mac_type mac;
+
+ /// MTU
+ size_t mtu;
+
+ /// Identificador de la cola a usar
+ int que_id;
+
+ /// Constructor
+ Dev(mac_type mac, key_t key = DEV_DEFAULT_KEY, size_t mtu = DEV_MAX_MTU)
+ throw (std::runtime_error, std::logic_error);
+
+ /// Envía un frame
+ void transmit(const std::string& data, const mac_type& mac)
+ throw (std::runtime_error, std::logic_error);
+
+ /// Recibe un frame
+ std::string receive()
+ throw (std::runtime_error);
+
+ // Nada de andar copiando placas...
+ private:
+ Dev(const Dev&);
+ Dev& operator=(const Dev&);
+
+};
+
+#endif // _DEV_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipout.h"
+#include "ipin.h"
+#include "ipaddr.h"
+#include "routetable.h"
+#include "dev.h"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <signal.h>
+
+// Uso: ./test_ipout ip [router forward routes_file queue_id proto]
+
+void send_loop(IPOut& ipout, unsigned proto);
+
+void add_routes(RouteTable& rt, std::istream& is, Dev& dev);
+
+int main(int argc, char* argv[])
+{
+ bool router = false;
+ bool forward = false;
+ unsigned proto = 0;
+ key_t queue_id = DEV_DEFAULT_KEY;
+ std::string fname = "route.txt";
+ if (argc < 2)
+ {
+ std::cerr << "Uso: ./test_ipout ip [router forward routes_file "
+ "queue_id proto]\n";
+ return 1;
+ }
+ IPAddr addr(argv[1]);
+ if (argc > 2)
+ router = atoi(argv[2]);
+ if (argc > 3)
+ forward = atoi(argv[3]);
+ if (argc > 4)
+ fname = argv[4];
+ if (argc > 5)
+ queue_id = atoi(argv[5]);
+ if (argc > 6)
+ proto = atoi(argv[6]);
+ // Creo colas
+ int que_id = msgget(queue_id, IPC_CREAT | 0666); assert(que_id != -1);
+ que_id = msgget(DEV_DEFAULT_KEY-1, IPC_CREAT | 0666); assert(que_id != -1);
+ // Abro archivo con rutas
+ std::ifstream ifs(fname.c_str()); assert(ifs);
+ // Creo medio físico y cola para forwarding
+ Dev dev(addr, queue_id);
+ Dev fwque(addr, DEV_DEFAULT_KEY-1);
+ // Creo procesos
+ pid_t pid_send = fork();
+ if (pid_send == -1)
+ {
+ perror("fork() send");
+ return 2;
+ }
+ if (pid_send) // IPOut
+ {
+ RouteTable table(dev);
+ add_routes(table, ifs, dev);
+ IPOut ipout(addr, table, fwque, std::cerr);
+ pid_t pid_fw = fork();
+ if (pid_fw == -1)
+ {
+ perror("fork() forward");
+ return 3;
+ }
+ if (pid_fw) // Padre (IPOut send)
+ {
+ int ret;
+ send_loop(ipout, proto);
+ kill(pid_send, SIGTERM);
+ waitpid(pid_send, &ret, 0);
+ kill(pid_fw, SIGTERM);
+ waitpid(pid_fw, &ret, 0);
+ return 0;
+ }
+ else // Hijo 1 (IPOut forward)
+ {
+ ipout.forward_loop();
+ return 0;
+ }
+ }
+ else // Hijo 2 (IPIn)
+ {
+ IPIn ipin(addr, dev, fwque, router, forward, std::cerr);
+ while (true)
+ {
+ IPAddr src, dst;
+ std::string s = ipin.recv(proto, src, dst);
+ std::cout << "Recibido '" << s << "' (len " << s.size() << ") de "
+ << src << " para " << dst << " (proto = " << proto << ")\n";
+ }
+ return 0;
+ }
+ return 0;
+}
+
+void send_loop(IPOut& ipout, unsigned proto)
+{
+ std::string dst;
+ std::string msg;
+ while (std::getline(std::cin, dst))
+ {
+ if (!std::getline(std::cin, msg))
+ break;
+ if (ipout.send(msg, proto, IPAddr(dst.c_str())))
+ std::cout << "Enviado '" << msg << "' a " << dst << "\n";
+ else
+ std::cout << "NO SE PUDO ENVIAR '" << msg << "' a " << dst << "\n";
+ }
+}
+
+void add_routes(RouteTable& rt, std::istream& is, Dev& dev)
+{
+ std::string line;
+ while (std::getline(is, line))
+ {
+ std::istringstream iss(line);
+ std::string net;
+ std::string gw;
+ unsigned mtu;
+ unsigned metric;
+ iss >> net >> gw >> mtu >> metric;
+ if (net == "0") net = "0.0.0.0";
+ if (gw == "0") gw = "0.0.0.0";
+ rt.add(net.c_str(), gw.c_str(), metric, mtu, dev);
+ }
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipaddr.h"
+#include <sstream>
+
+/// Constructor
+IPAddr::IPAddr()
+{
+ atoms[0] = 0;
+ atoms[1] = 0;
+ atoms[2] = 0;
+ atoms[3] = 0;
+}
+
+/// Constructor
+IPAddr::IPAddr(atom a1, atom a2, atom a3, atom a4)
+{
+ atoms[0] = a1;
+ atoms[1] = a2;
+ atoms[2] = a3;
+ atoms[3] = a4;
+}
+
+/// Constructor
+IPAddr::IPAddr(int ip)
+{
+ atoms[0] = ip >> 24;
+ atoms[1] = ip >> 16;
+ atoms[2] = ip >> 8;
+ atoms[3] = ip;
+}
+
+/// Constructor
+IPAddr::IPAddr(const char* ip) throw (std::logic_error)
+{
+ std::istringstream iss(ip);
+ std::string ips;
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!std::getline(iss, ips, '.'))
+ throw std::logic_error("Dirección IP inválida");
+ atoms[i] = std::atoi(ips.c_str());
+ }
+}
+
+/// Constructor
+//IPAddr::IPAddr(const std::string& ip) throw (std::logic_error)
+//{
+// IPAddr(ip.c_str());
+//}
+
+/// Operador de casteo a unsigned
+IPAddr::operator unsigned () const
+{
+ return (atoms[0] << 24) + (atoms[1] << 16) + (atoms[2] << 8) + atoms[3];
+}
+
+/// Operador de casteo a std::string
+IPAddr::operator std::string () const
+{
+ std::ostringstream oss;
+ oss << unsigned(atoms[0]) << "." << unsigned(atoms[1]) << "."
+ << unsigned(atoms[2]) << "." << unsigned(atoms[3]);
+ return oss.str();
+}
+
+std::ostream& operator<< (std::ostream& os, const IPAddr& ip)
+{
+ return os << std::string(ip);
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPADDR_H_
+#define _IPADDR_H_
+
+#include <string>
+#include <cstdlib>
+#include <stdexcept>
+
+/// Dirección IP
+struct IPAddr
+{
+
+ /// Átomo de dirección IP
+ typedef unsigned char atom;
+
+ /// Representación interna
+ atom atoms[4];
+
+ /// Constructor
+ IPAddr();
+
+ /// Constructor
+ IPAddr(atom a1, atom a2, atom a3, atom a4);
+
+ /// Constructor
+ IPAddr(int ip);
+
+ /// Constructor
+ IPAddr(const char* ip) throw (std::logic_error);
+
+ /// Constructor
+ //IPAddr(const std::string& ip) throw (std::logic_error);
+
+ /// Operador de casteo a unsigned
+ operator unsigned () const;
+
+ /// Operador de casteo a std::string
+ operator std::string () const;
+
+};
+
+std::ostream& operator<< (std::ostream& os, const IPAddr& ip);
+
+#endif // _IPADDR_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#include "ipheader.h"
+
+IPHeader::IPHeader(uint8_t version, uint16_t total_len, uint16_t id, bool df,
+ bool mf, uint16_t offset, uint8_t ttl, uint8_t proto,
+ const IPAddr& src, const IPAddr& dst):
+ version(version), total_len(total_len), id(id), reserved_flag(0),
+ df(df), mf(mf), offset(offset), ttl(ttl), proto(proto), checksum(0),
+ src(src), dst(dst)
+{
+ do_checksum();
+}
+
+IPHeader::IPHeader(const std::string& s)
+{
+ *this = *((IPHeader*)s.c_str());
+}
+
+size_t IPHeader::header_len()
+{
+ return sizeof(IPHeader);
+}
+
+bool IPHeader::check_checksum() const
+{
+ IPHeader iph = *this;
+ iph.checksum = 0;
+ char* raw = (char*) &iph;
+ uint16_t sum = 0;
+ for (unsigned i = 0; i < sizeof(IPHeader); ++i)
+ sum += raw[i];
+ return sum == checksum;
+}
+
+void IPHeader::do_checksum()
+{
+ checksum = 0;
+ char* raw = (char*) this;
+ uint16_t sum = 0;
+ for (unsigned i = 0; i < sizeof(IPHeader); ++i)
+ sum += raw[i];
+ checksum = sum;
+}
+
+std::ostream& operator<<(std::ostream& os, const IPHeader& iph)
+{
+ return os
+ << "version=" << unsigned(iph.version)
+ << " total_len=" << iph.total_len
+ << " id=" << iph.id
+ << " DF=" << bool(iph.df)
+ << " MF=" << bool(iph.mf)
+ << " offset=" << unsigned(iph.offset)
+ << " TTL=" << unsigned(iph.ttl)
+ << " proto=" << unsigned(iph.proto)
+ << " checksum=" << iph.checksum
+ << " src=" << IPAddr(iph.src)
+ << " dst=" << IPAddr(iph.dst);
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPHEADER_H_
+#define _IPHEADER_H_
+
+#include "ipaddr.h"
+#include <string>
+#include <ostream>
+#include <stdint.h>
+
+/// Dispositivo de red (capa de enlace)
+struct IPHeader
+{
+
+ // Campos
+ uint8_t version;
+ //TODO IHL
+ //TODO TOS
+ uint16_t total_len;
+ uint16_t id;
+ uint16_t reserved_flag: 1;
+ uint16_t df: 1;
+ uint16_t mf: 1;
+ uint16_t offset: 13;
+ uint8_t ttl;
+ uint8_t proto;
+ uint16_t checksum;
+ uint32_t src;
+ uint32_t dst;
+
+ IPHeader(uint8_t version, uint16_t total_len, uint16_t id, bool df,
+ bool mf, uint16_t offset, uint8_t ttl, uint8_t proto,
+ const IPAddr& src, const IPAddr& dst);
+
+ IPHeader(const std::string& s);
+
+ static size_t header_len();
+
+ bool check_checksum() const;
+
+ void do_checksum();
+
+};
+
+std::ostream& operator<<(std::ostream& os, const IPHeader& iph);
+
+#endif // _IPHEADER_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipin.h"
+#include "ipheader.h"
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+/// Constructor
+IPIn::IPIn(const IPAddr& ip, Dev& dev, Dev& forward_que, bool router,
+ bool forward, std::ostream& log):
+ ip(ip), dev(dev), forward_que(forward_que), router(router),
+ forward(forward), log(log)
+{
+ if (router) forward = true;
+}
+
+void IPIn::drop(const std::string& msg, const std::string& buf)
+{
+ log << "IPIn::drop (" << ip << "): " << msg << "\n\tBuffer: " << buf
+ << "\n";
+}
+
+void IPIn::drop(const std::string& msg, const IPHeader& iph)
+{
+ log << "IPIn::drop (" << ip << "): " << msg << "\n\tIPHeader: " << iph
+ << "\n";
+}
+
+/// Recibe un paquete IP
+std::string IPIn::recv(uint8_t proto, IPAddr& src, IPAddr& dst) throw (std::runtime_error)
+{
+ while (true)
+ {
+ std::string buf = dev.receive();
+ // No es siquiera IP
+ if (buf.size() < IPHeader::header_len())
+ {
+ // Silencioso
+ drop("Cabecera incompleta o no es IP", buf);
+ continue;
+ }
+ IPHeader iph(buf);
+#ifdef DEBUG
+ std::cout << "IPIn::recv (" << ip << "): IPHeader: " << iph << "\n";
+ std::string tmp = buf.substr(iph.header_len());
+ std::cout << "\tdata (" << tmp.size() << ") = " << tmp << "\n";
+#endif
+ if (iph.version != 4)
+ {
+ // Silencioso
+ drop("Versión IP incorrecta", iph);
+ continue;
+ }
+ if (!iph.check_checksum())
+ {
+ // Silencioso
+ drop("Mal checksum", iph);
+ continue;
+ }
+ // Si el TTL se va a 0
+ if (!--iph.ttl)
+ {
+ // ICMP
+ drop("TTL == 0 -> ICMP", iph);
+ continue;
+ }
+ // No es para nosotros y no forwardeamos
+ if (iph.dst != ip && !forward)
+ {
+ // Silencioso
+ drop("No es para nosotros y no hacemos forward", iph);
+ continue;
+ }
+ // No es para nosotros pero forwardeamos
+ else if (iph.dst != ip)
+ {
+ forward_que.transmit(buf, ip);
+ continue;
+ }
+ // Es para nosotros pero somos router
+ else if (router)
+ {
+ // Silencioso
+ drop("Es para nosotros pero somos un router", iph);
+ continue;
+ }
+ // Es para nosotros y somos un host
+ // Guarda en buffer
+ buffer[iph][iph.offset] = buf.substr(iph.header_len());
+ // Si tiene más fragmentos o es un protocolo distinto, sigo
+ if (iph.mf || (iph.proto != proto))
+ continue;
+ // No hay más fragmentos, reensamblamos (de ser necesario)
+ std::string data;
+ for (offsetmap_type::iterator i = buffer[iph].begin();
+ i != buffer[iph].end(); ++i)
+ {
+ //TODO chequear que los fragmentos estén todos
+ data += i->second;
+ }
+#ifdef DEBUG
+ std::cout << "IPIn::recv (" << ip << "): Paquete completo: data = '"
+ << data << "'\n";
+#endif
+ buffer.erase(iph);
+ //TODO faltaría limpiar fragmentos viejos cada tanto (timer?)
+ src = iph.src;
+ dst = iph.dst;
+ return data;
+ }
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPIN_H_
+#define _IPIN_H_
+
+#include "ipaddr.h"
+#include "ipheader.h"
+#include "dev.h"
+#include <map>
+#include <string>
+#include <iostream>
+#include <stdexcept>
+
+/// IP de recepción
+struct IPIn
+{
+
+ /// Dirección IP
+ IPAddr ip;
+
+ /// Dispositivo de red
+ Dev& dev;
+
+ /// Cola para forwardear paquetes
+ Dev& forward_que;
+
+ /// Indica si es un router
+ bool router;
+
+ /// Indica si hace forwarding
+ bool forward;
+
+ /// Dispositivo de logging
+ std::ostream& log;
+
+ /// Buffers de recepción
+ struct BufferKey
+ {
+ uint16_t id;
+ uint32_t src, dst;
+ uint8_t proto;
+ BufferKey(const IPHeader& h):
+ id(h.id), src(h.src), dst(h.dst), proto(h.proto)
+ {}
+ bool operator< (const BufferKey& b) const
+ { return id < b.id && src < b.src && dst < b.dst && proto < b.proto; }
+ };
+ typedef std::map< uint16_t, std::string > offsetmap_type;
+ typedef std::map< BufferKey, offsetmap_type > buffer_type;
+ buffer_type buffer;
+
+ /// Constructor
+ IPIn(const IPAddr& ip, Dev& dev, Dev& forward_que, bool router = false,
+ bool forward = false, std::ostream& log = std::cout);
+
+ /// Descarta un paquete
+ void drop(const std::string& msg, const std::string& buf);
+ void drop(const std::string& msg, const IPHeader& iph);
+
+ /// Recibe un paquete IP
+ std::string recv(uint8_t proto, IPAddr& src, IPAddr& dst)
+ throw (std::runtime_error);
+
+ // Nada de andar copiando placas...
+ private:
+ IPIn(const IPIn&);
+ IPIn& operator=(const IPIn&);
+
+};
+
+#endif // _IPIN_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipout.h"
+#include "ipheader.h"
+#include <ctime>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+/// Constructor
+IPOut::IPOut(const IPAddr& ip, RouteTable& rtable, Dev& forward_que, std::ostream& log):
+ ip(ip), rtable(rtable), forward_que(forward_que), log(log)
+{
+}
+
+void IPOut::drop(const std::string& msg, const std::string& buf)
+{
+ log << "IPOut::drop (" << ip << "): " << msg << "\n\tBuffer: " << buf
+ << "\n";
+}
+
+void IPOut::drop(const std::string& msg, const IPHeader& iph)
+{
+ log << "IPOut::drop (" << ip << "): " << msg << "\n\tIPHeader: " << iph
+ << "\n";
+}
+
+/// Envía un paquete IP
+bool IPOut::send(const std::string& data, uint8_t proto, IPAddr dst, IPAddr src,
+ bool df, uint8_t ttl, uint16_t id)
+ throw (std::runtime_error)
+{
+ // Armamos cabecera
+ if (!src)
+ src = ip;
+ if (!id)
+ id = get_id();
+ IPHeader iph(4, IPHeader::header_len() + data.size(), id, df, 0, 0,
+ ttl, proto, src, dst);
+ // Enviamos
+ return send(iph, data);
+}
+
+/// Envía un paquete IP
+bool IPOut::send(IPHeader iph, std::string data) throw (std::runtime_error)
+{
+ // Buscamos ruta
+ RouteTable::Route* r = rtable.get(iph.dst);
+ if (!r)
+ {
+ // ICMP
+ drop("No existe una ruta para el destino -> ICMP", iph);
+ return false;
+ }
+ // No quieren fragmentar
+ if (iph.df && (IPHeader::header_len() + data.size() > r->mtu))
+ {
+ // Silencioso
+ drop("Tamaño de paquete más grande que MTU y DF=1", iph);
+ return false;
+ }
+ // Fragmenta (de ser necesario)
+ int max_payload = r->mtu - IPHeader::header_len();
+ int cant_frag = data.size() / max_payload;
+ if (data.size() % max_payload)
+ ++cant_frag;
+ for (int i = 0; i < cant_frag; ++i)
+ {
+ IPHeader iph2 = iph;
+ if (i != (cant_frag - 1))
+ iph2.mf = 1;
+ iph2.offset += i * max_payload;
+ iph2.total_len -= i * max_payload;
+ iph2.do_checksum();
+ std::string buf((char*) &iph2, sizeof(IPHeader));
+ buf += data.substr(i * max_payload, max_payload);
+#ifdef DEBUG
+ std::cout << "IPOut::send (" << ip << "): Fragmento " << i
+ << " => IPHeader: " << iph2 << "\n";
+ std::string tmp = data.substr(i * max_payload, max_payload);
+ std::cout << "\tdata (" << tmp.size() << ") = " << tmp << "\n";
+#endif
+ r->iface->transmit(buf, r->gateway ? r->gateway : IPAddr(iph.dst));
+ }
+ return true;
+}
+
+/// Realiza el forwarding de paquetes (en un loop infinito)
+void IPOut::forward_loop()
+ throw (std::runtime_error)
+{
+ while (true)
+ {
+ std::string buf = forward_que.receive();
+ IPHeader iph(buf);
+#ifdef DEBUG
+ std::cout << "IPOut::forward_loop (" << ip << "): A forwardear (id "
+ << iph.id << ")\n";
+#endif
+ send(iph, buf.substr(iph.header_len()));
+ }
+}
+
+/// Obtiene un identificador para el paquete
+uint16_t IPOut::get_id() const
+{
+ static uint16_t st = time(NULL);
+ uint16_t tt = time(NULL);
+ return (tt == st) ? ++st : tt;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPOUT_H_
+#define _IPOUT_H_
+
+#include "ipaddr.h"
+#include "ipheader.h"
+#include "routetable.h"
+#include <iostream>
+#include <string>
+#include <stdexcept>
+
+/// IP de envío
+struct IPOut
+{
+
+ /// Dirección MAC
+ IPAddr ip;
+
+ /// Dispositivo de logging
+ RouteTable& rtable;
+
+ /// Cola para forwardear paquetes
+ Dev& forward_que;
+
+ /// Dispositivo de logging
+ std::ostream& log;
+
+ /// Constructor
+ IPOut(const IPAddr& ip, RouteTable& rtable, Dev& forward_que,
+ std::ostream& log = std::cout);
+
+ /// Descarta un paquete
+ void drop(const std::string& msg, const std::string& buf);
+ void drop(const std::string& msg, const IPHeader& iph);
+
+ /// Envía un paquete IP a armar (y forwardea los encolados, de haber)
+ bool send(const std::string& data, uint8_t proto, IPAddr dst,
+ IPAddr src = 0, bool df = 0, uint8_t ttl = 64, uint16_t id = 0)
+ throw (std::runtime_error);
+
+ /// Envía un paquete IP ya armado
+ bool send(IPHeader iph, std::string data) throw (std::runtime_error);
+
+ /// Realiza el forwarding de paquetes (en un loop infinito)
+ void forward_loop() throw (std::runtime_error);
+
+ /// Obtiene un identificador para el paquete
+ uint16_t get_id() const;
+
+ /// Se fija si hay paquetes a forwardear (y devuelve cuantos hay)
+ unsigned to_forward();
+
+ // Nada de andar copiando...
+ private:
+ IPOut(const IPOut&);
+ IPOut& operator=(const IPOut&);
+
+};
+
+#endif // _IPOUT_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#include "routetable.h"
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+RouteTable::RouteTable(Dev& default_iface): default_iface(default_iface)
+{
+}
+
+void RouteTable::add(const IPAddr& net, const IPAddr& gw, unsigned mtu,
+ unsigned metric, Dev& iface)
+{
+ table[net] = Route(gw, metric, mtu, iface);
+#ifdef DEBUG
+ //std::cout << "Se agregó tabla para " << net << ": gw = " << gw
+ // << ", metric = " << metric << "\n";
+#endif
+}
+
+void RouteTable::del(const IPAddr& net)
+{
+ table.erase(net);
+}
+
+RouteTable::Route* RouteTable::get(const IPAddr& dst)
+{
+ // No existe
+ if (table.find(dst) == table.end())
+ return 0;
+ return &table[dst];
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _ROUTETABLE_H_
+#define _ROUTETABLE_H_
+
+#include "dev.h"
+#include "ipaddr.h"
+#include <map>
+
+/// Tabla de ruteo
+struct RouteTable
+{
+
+ /// Ruta
+ struct Route
+ {
+ IPAddr gateway;
+ unsigned mtu;
+ unsigned metric;
+ Dev* iface;
+ Route(): gateway(0), mtu(0), metric(0), iface(0) {}
+ Route(const IPAddr& gateway, unsigned mtu, unsigned metric, Dev& iface):
+ gateway(gateway), mtu(mtu), metric(metric), iface(&iface) {}
+ };
+
+ /// Tabla
+ std::map< IPAddr, Route > table;
+
+ /// Interfaz por default
+ Dev& default_iface;
+
+ /// Constructor
+ RouteTable(Dev& default_iface);
+
+ /// Agrega ruta
+ void add(const IPAddr& net, const IPAddr& gw, unsigned mtu, unsigned metric, Dev& iface);
+
+ /// Borra ruta
+ void del(const IPAddr& net);
+
+ /// Obtiene dirección e interfaz por la cual salir para un destino
+ Route* get(const IPAddr& dst);
+
+};
+
+#endif // _ROUTETABLE_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#!/bin/bash
+# 35 28 25
+# 10.10.10.1 ------ 10.10.10.5 ------ 10.10.10.4 ------ 10.10.10.2
+# |
+# | 32
+# |
+# 10.10.10.3
+#
+
+# Host 10.10.10.1
+(echo -e '10.10.10.3\nAdios mundo cruel!!!'; sleep 1) \
+ | ./ip 10.10.10.1 0 0 ../rutas_ejemplo/route_10.10.10.1.txt &
+
+# Host 10.10.10.5
+(echo ; sleep 2) \
+ | ./ip 10.10.10.5 1 1 ../rutas_ejemplo/route_10.10.10.5.txt &
+
+# Host 10.10.10.3
+(echo ; sleep 3) \
+ | ./ip 10.10.10.3 0 0 ../rutas_ejemplo/route_10.10.10.3.txt &
+
+# Limpio
+sleep 4
+ipcrm -Q 0x1abcdef1
+ipcrm -Q 0x1abcdef0
--- /dev/null
+#!/bin/bash
+# 35 28 25
+# 10.10.10.1 ------ 10.10.10.5 ------ 10.10.10.4 ------ 10.10.10.2
+# |
+# | 32
+# |
+# 10.10.10.3
+#
+
+# Host 10.10.10.1
+(echo -e '10.10.10.2\nAdios mundo cruel!!!'; sleep 1) \
+ | ./ip 10.10.10.1 0 0 ../rutas_ejemplo/route_10.10.10.1.txt &
+
+# Host 10.10.10.5
+(echo ; sleep 2) \
+ | ./ip 10.10.10.5 1 1 ../rutas_ejemplo/route_10.10.10.5.txt &
+
+# Host 10.10.10.4
+(echo ; sleep 3) \
+ | ./ip 10.10.10.4 1 1 ../rutas_ejemplo/route_10.10.10.4.txt &
+
+# Host 10.10.10.2
+(echo ; sleep 4) \
+ | ./ip 10.10.10.2 0 0 ../rutas_ejemplo/route_10.10.10.2.txt &
+
+# Limpio
+sleep 5
+ipcrm -Q 0x1abcdef1
+ipcrm -Q 0x1abcdef0
+
--- /dev/null
+#include "ipaddr.h"
+#include "ipheader.h"
+#include <iostream>
+
+int main()
+{
+ // Addr
+ IPAddr ip1(0x0a0a0a05);
+ IPAddr ip2("10.10.10.1");
+ IPAddr ip3(10, 10, 10, 2);
+ std::cout << "IP1 = " << ip1 << "\n";
+ std::cout << "IP2 = " << ip2 << "\n";
+ std::cout << "IP3 = " << ip3 << "\n";
+ // Header
+ IPHeader h1(4, 20, 1, 1, 0, 0, 64, 0x11, ip1, ip2);
+ std::cout << "Header1 = " << h1 << "\n";
+ if (h1.check_checksum())
+ std::cout << "Checksum OK\n";
+ else
+ std::cout << "Checksum MAL!\n";
+ h1.checksum = 1;
+ if (h1.check_checksum())
+ std::cout << "Checksum OK\n";
+ else
+ std::cout << "Checksum MAL!\n";
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipin.h"
+#include "ipaddr.h"
+#include "dev.h"
+#include <iostream>
+#include <cstdlib>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+// Uso: ./test_ipin ip mtu router forward proto queue_id
+
+int main(int argc, char* argv[])
+{
+ IPAddr addr("10.10.10.1");
+ unsigned mtu = 25;
+ bool router = false;
+ bool forward = false;
+ unsigned proto = 0;
+ key_t queue_id = DEV_DEFAULT_KEY;
+ if (argc > 1)
+ addr = IPAddr(argv[1]);
+ if (argc > 2)
+ mtu = atoi(argv[2]);
+ if (argc > 3)
+ router = atoi(argv[3]);
+ if (argc > 4)
+ forward = atoi(argv[4]);
+ if (argc > 5)
+ proto = atoi(argv[5]);
+ if (argc > 6)
+ queue_id = atoi(argv[6]);
+ int que_id = msgget(queue_id, IPC_CREAT | 0666);
+ assert(que_id != -1);
+ Dev dev(addr, mtu, queue_id);
+ que_id = msgget(queue_id+1, IPC_CREAT | 0666);
+ assert(que_id != -1);
+ Dev fwque(addr, DEV_MAX_MTU, queue_id+1);
+ IPIn ipin(addr, dev, fwque, router, forward, std::cerr);
+ struct msqid_ds minfo;
+ for (msgctl(dev.que_id, IPC_STAT, &minfo); minfo.msg_qnum;
+ msgctl(dev.que_id, IPC_STAT, &minfo))
+ {
+ IPAddr src, dst;
+ std::cout << "Quedan " << minfo.msg_qnum << " mensajes en la cola\n";
+ std::string s = ipin.recv(proto, src, dst);
+ std::cout << "Recibido '" << s << "' (len " << s.size() << ") de "
+ << src << " para " << dst << " (proto = " << proto << ")\n";
+ }
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipout.h"
+#include "ipaddr.h"
+#include "routetable.h"
+#include "dev.h"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+// Uso: ./test_ipout ip dst mtu routes_file proto queue_id
+
+void add_routes(RouteTable& rt, std::istream& is, Dev& dev);
+
+int main(int argc, char* argv[])
+{
+ IPAddr addr("10.10.10.2");
+ IPAddr dst("10.10.10.1");
+ unsigned mtu = 25;
+ unsigned proto = 0;
+ key_t queue_id = DEV_DEFAULT_KEY;
+ std::string fname = "route.txt";
+ if (argc > 1)
+ addr = IPAddr(argv[1]);
+ if (argc > 2)
+ dst = IPAddr(argv[2]);
+ if (argc > 3)
+ mtu = atoi(argv[3]);
+ if (argc > 4)
+ fname = argv[4];
+ if (argc > 5)
+ proto = atoi(argv[5]);
+ if (argc > 6)
+ queue_id = atoi(argv[6]);
+ int que_id = msgget(queue_id, IPC_CREAT | 0666); assert(que_id != -1);
+ que_id = msgget(queue_id+1, IPC_CREAT | 0666); assert(que_id != -1);
+ std::ifstream ifs(fname.c_str()); assert(ifs);
+ Dev dev(addr, mtu, queue_id);
+ Dev fwque(addr, DEV_MAX_MTU, queue_id+1);
+ RouteTable table(dev);
+ add_routes(table, ifs, dev);
+ IPOut ipout(addr, table, fwque, std::cerr);
+ std::string msg;
+ while (std::getline(std::cin, msg))
+ {
+ if (ipout.send(msg, proto, dst))
+ std::cout << "Enviado '" << msg << "' a " << dst << "\n";
+ else
+ std::cout << "NO SE PUDO ENVIAR '" << msg << "' a " << dst << "\n";
+ }
+ return 0;
+}
+
+void add_routes(RouteTable& rt, std::istream& is, Dev& dev)
+{
+ std::string line;
+ while (std::getline(is, line))
+ {
+ std::istringstream iss(line);
+ std::string net;
+ std::string gw;
+ unsigned metric;
+ iss >> net >> gw >> metric;
+ if (net == "0") net = "0.0.0.0";
+ if (gw == "0") gw = "0.0.0.0";
+ rt.add(net.c_str(), gw.c_str(), metric, dev);
+ }
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "dev.h"
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+int main()
+{
+ Dev dev(4321);
+ struct msqid_ds minfo;
+ for (msgctl(dev.que_id, IPC_STAT, &minfo); minfo.msg_qnum;
+ msgctl(dev.que_id, IPC_STAT, &minfo))
+ {
+ std::cout << "Quedan " << minfo.msg_qnum << " mensajes en la cola\n";
+ std::string s = dev.receive();
+ std::cout << "Recibido '" << s << "' (len " << s.size() << ")\n";
+ }
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "dev.h"
+#include <iostream>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+int main()
+{
+ int que_id = msgget(DEV_DEFAULT_KEY, IPC_CREAT | 0666);
+ assert(que_id != -1);
+ Dev dev(1234);
+ dev.transmit("hola mundo", 4321);
+ std::cout << "Enviado 'hola mundo' a 4321\n";
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+Enunciado extraoficial
+
+Hay que hacer 2 procesos, uno que manda y otro que recibe IP, por cada
+host/router. Todos los procesos que envian, ponen las cosas en una cola, todos
+los que reciben, sacan de esa cola. Se usa como MAC la IP, y como ID del mensaje
+de la cola (de esta manera cada proceso saca solo los "paquetes" con el ID/MAC
+que le corresponda).
+
+1) Campos en IP
+ id de paquete
+ ip origen
+ ip destino
+ checksum (0/1, de juguete)
+ tamaño del paquete completo
+ ToS
+ Don't Fragment (0/1)
+ End (0/1)
+ offset
+ TTL
+ tamaño de este fragmento
+ tipo de payload (IP / ICMP)
+ (se que faltan algunos, si tienen algo mas,
+ completen)
+
+2) Casos de descarte de paquetes
+ Error de checksum (silencioso)
+ No hay buffer para fragmento (silencioso)
+ Un host que no rutea recibe un paquete para otro host
+ (silencioso)
+ No hay ruta (icmp)
+ DF == 1 y MTU < size (icmp)
+ TTL == 0 (icmp)
+
+3) Comportamiento del protocolo
+ Debe rutear (si es un router)
+ Debe fragmentar y reensamblar
+ Debe contemplar todos los casos de descarte de
+ paquetes anteriores escribiendo en un archivo
+ los paquetes descartados según corresponda
+ (silecioso, icmp).
+
--- /dev/null
+===============================
+Sistemas Distribuidos I (75.74)
+===============================
+
+----------------------------
+Trabajo práctico de stack IP
+----------------------------
+
+: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.
+
+
+Uso
+===
+
+El trabajo tiene 2 programas de prueba principales: `test_ipin` y `test_ipout`,
+ambos generados en el directorio `src` luego de compilar el código.
+
+test_ipin
+---------
+
+Este programa es el proceso que recibe paquetes IP y los procesa.
+
+Uso::
+
+ ./test_ipin [ip] [mtu] [router] [forward] [proto] [queue_id]
+
+ip
+ IP que utiliza este proceso (default 10.10.10.1)
+
+mtu
+ MTU de la capa física de este proceso (default 25)
+
+router
+ 0 si es router, 1 si no lo es (default 0)
+
+forward
+ 0 si puede hacer forwarding, 1 si no (default 0)
+
+proto
+ Protocolo que transporta (default 0)
+
+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`)
+
+
+test_ipout
+----------
+
+Este programa es el proceso que envía paquetes IP y rutea.
+
+Uso::
+
+ ./test_ipout [ip] [dst] [mtu] [routes_file] [proto] [queue_id]
+
+ip
+ IP que utiliza este proceso (default 10.10.10.2)
+
+dst
+ IP del destino al cual mandar paquetes (default 10.10.10.1)
+
+mtu
+ MTU de la capa física de este proceso (default 25)
+
+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) y métrica (todavía no se usa), separados
+ por uno o más espacios o tabs (default `route.txt`)
+
+proto
+ Protocolo que transporta (default 0)
+
+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`)
+
+
+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. Utiliza 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.
+
+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, 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
+ * 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
+
+
+Ejemplo de corrida
+==================
+
+Host 10.10.10.1
+---------------
+
+Rutas:
+
+* 10.10.10.1 0.0.0.0 0
+* 10.10.10.3 10.10.10.5 0
+* 10.10.10.5 0.0.0.0 0
+
+Envía "adios mundo cruel!!!" al host 10.10.10.3::
+
+ $ ./test_ipout 10.10.10.1 10.10.10.3 25 ../rutas_ejemplo/route_10.10.10.1.txt
+ Se agregó tabla para 10.10.10.1: gw = 0.0.0.0, metric = 0
+ Se agregó tabla para 10.10.10.2: gw = 0.0.0.0, metric = 0
+ Se agregó tabla para 10.10.10.3: gw = 10.10.10.5, metric = 0
+ Se agregó tabla para 10.10.10.5: gw = 0.0.0.0, metric = 0
+ adios mundo cruel!!!
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=40 id=56121 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=196 src=10.10.10.1 dst=10.10.10.3
+ data (5) = adios
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=35 id=56121 DF=0 MF=1 offset=5 TTL=64 proto=0 checksum=231 src=10.10.10.1 dst=10.10.10.3
+ data (5) = mund
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=30 id=56121 DF=0 MF=1 offset=10 TTL=64 proto=0 checksum=266 src=10.10.10.1 dst=10.10.10.3
+ data (5) = o cru
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=25 id=56121 DF=0 MF=0 offset=15 TTL=64 proto=0 checksum=297 src=10.10.10.1 dst=10.10.10.3
+ data (5) = el!!!
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ Enviado 'adios mundo cruel!!!' a 10.10.10.3
+
+El proceso test_ipin de este host no tiene relevancia.
+
+Router 10.10.10.5
+-----------------
+
+Rutas:
+
+* 10.10.10.1 0.0.0.0 0
+* 10.10.10.3 0.0.0.0 0
+* 10.10.10.5 0.0.0.0 0
+
+Recibe el mensaje::
+
+ $ ./test_ipin 10.10.10.5 25 1 1
+ Quedan 4 mensajes en la cola
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPIn::recv: IPHeader: version= total_len=40 id=56121 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=196 src=10.10.10.1 dst=10.10.10.3
+ data (5) = adios
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPIn::recv: IPHeader: version= total_len=35 id=56121 DF=0 MF=1 offset=5 TTL=64 proto=0 checksum=231 src=10.10.10.1 dst=10.10.10.3
+ data (5) = mund
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPIn::recv: IPHeader: version= total_len=30 id=56121 DF=0 MF=1 offset=10 TTL=64 proto=0 checksum=266 src=10.10.10.1 dst=10.10.10.3
+ data (5) = o cru
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPIn::recv: IPHeader: version= total_len=25 id=56121 DF=0 MF=0 offset=15 TTL=64 proto=0 checksum=297 src=10.10.10.1 dst=10.10.10.3
+ data (5) = el!!!
+ Dev::transmit(msgtype/mac = 168430085, size = 25)
+
+Y lo forwardea (refragmentando porque tiene un MTU más pequeño)::
+
+ $ ./test_ipout 10.10.10.5 10.10.10.1 23 ../rutas_ejemplo/route_10.10.10.5.txt
+ Se agregó tabla para 10.10.10.1: gw = 0.0.0.0, metric = 0
+ Se agregó tabla para 10.10.10.2: gw = 0.0.0.0, metric = 0
+ Se agregó tabla para 10.10.10.3: gw = 0.0.0.0, metric = 0
+ Se agregó tabla para 10.10.10.5: gw = 0.0.0.0, metric = 0
+ a
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=21 id=56140 DF=0 MF=0 offset=0 TTL=64 proto=0 checksum=194 src=10.10.10.5 dst=10.10.10.1
+ data (1) = a
+ Dev::transmit(msgtype/mac = 168430081, size = 21)
+ Enviado 'a' a 10.10.10.1
+ a
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPOut::send: A forwardear
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=40 id=56121 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=196 src=10.10.10.1 dst=10.10.10.3
+ data (3) = adi
+ Dev::transmit(msgtype/mac = 168430083, size = 23)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=37 id=56121 DF=0 MF=1 offset=3 TTL=64 proto=0 checksum=217 src=10.10.10.1 dst=10.10.10.3
+ data (2) = os
+ Dev::transmit(msgtype/mac = 168430083, size = 22)
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPOut::send: A forwardear
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=35 id=56121 DF=0 MF=1 offset=5 TTL=64 proto=0 checksum=231 src=10.10.10.1 dst=10.10.10.3
+ data (3) = mu
+ Dev::transmit(msgtype/mac = 168430083, size = 23)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=32 id=56121 DF=0 MF=1 offset=8 TTL=64 proto=0 checksum=252 src=10.10.10.1 dst=10.10.10.3
+ data (2) = nd
+ Dev::transmit(msgtype/mac = 168430083, size = 22)
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPOut::send: A forwardear
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=30 id=56121 DF=0 MF=1 offset=10 TTL=64 proto=0 checksum=266 src=10.10.10.1 dst=10.10.10.3
+ data (3) = o c
+ Dev::transmit(msgtype/mac = 168430083, size = 23)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=27 id=56121 DF=0 MF=1 offset=13 TTL=64 proto=0 checksum=287 src=10.10.10.1 dst=10.10.10.3
+ data (2) = ru
+ Dev::transmit(msgtype/mac = 168430083, size = 22)
+ Dev::receive(msgtype/mac = 168430085, size = 25)
+ IPOut::send: A forwardear
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=25 id=56121 DF=0 MF=1 offset=15 TTL=64 proto=0 checksum=301 src=10.10.10.1 dst=10.10.10.3
+ data (3) = el!
+ Dev::transmit(msgtype/mac = 168430083, size = 23)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=22 id=56121 DF=0 MF=0 offset=18 TTL=64 proto=0 checksum=62 src=10.10.10.1 dst=10.10.10.3
+ data (2) = !!
+ Dev::transmit(msgtype/mac = 168430083, size = 22)
+ IPOut::send: Fragmento 0 => IPHeader: version= total_len=21 id=56147 DF=0 MF=0 offset=0 TTL=64 proto=0 checksum=201 src=10.10.10.5 dst=10.10.10.1
+ data (1) = a
+ Dev::transmit(msgtype/mac = 168430081, size = 21)
+ Enviado 'a' a 10.10.10.1
+
+(notar que para que haga el forwarding se tuvo que enviar un paquete
+*dummy* para que busque en la cola de paquetes a forwardear (es un error
+de diseño que tendré que corregir de alguna forma)
+
+Host 10.10.10.3
+---------------
+
+Rutas:
+
+* 10.10.10.1 10.10.10.5 0
+* 10.10.10.3 0.0.0.0 0
+* 10.10.10.5 0.0.0.0 0
+
+Finalmente este host recibe todos los fragmentos, reensabla y pasa el
+ paquete completo a la capa superior::
+
+ $ ./test_ipin 10.10.10.3 23
+ Quedan 8 mensajes en la cola
+ Dev::receive(msgtype/mac = 168430083, size = 23)
+ IPIn::recv: IPHeader: version= total_len=40 id=56121 DF=0 MF=1 offset=0 TTL=64 proto=0 checksum=196 src=10.10.10.1 dst=10.10.10.3
+ data (3) = adi
+ Dev::receive(msgtype/mac = 168430083, size = 22)
+ IPIn::recv: IPHeader: version= total_len=37 id=56121 DF=0 MF=1 offset=3 TTL=64 proto=0 checksum=217 src=10.10.10.1 dst=10.10.10.3
+ data (2) = os
+ Dev::receive(msgtype/mac = 168430083, size = 23)
+ IPIn::recv: IPHeader: version= total_len=35 id=56121 DF=0 MF=1 offset=5 TTL=64 proto=0 checksum=231 src=10.10.10.1 dst=10.10.10.3
+ data (3) = mu
+ Dev::receive(msgtype/mac = 168430083, size = 22)
+ IPIn::recv: IPHeader: version= total_len=32 id=56121 DF=0 MF=1 offset=8 TTL=64 proto=0 checksum=252 src=10.10.10.1 dst=10.10.10.3
+ data (2) = nd
+ Dev::receive(msgtype/mac = 168430083, size = 23)
+ IPIn::recv: IPHeader: version= total_len=30 id=56121 DF=0 MF=1 offset=10 TTL=64 proto=0 checksum=266 src=10.10.10.1 dst=10.10.10.3
+ data (3) = o c
+ Dev::receive(msgtype/mac = 168430083, size = 22)
+ IPIn::recv: IPHeader: version= total_len=27 id=56121 DF=0 MF=1 offset=13 TTL=64 proto=0 checksum=287 src=10.10.10.1 dst=10.10.10.3
+ data (2) = ru
+ Dev::receive(msgtype/mac = 168430083, size = 23)
+ IPIn::recv: IPHeader: version= total_len=25 id=56121 DF=0 MF=1 offset=15 TTL=64 proto=0 checksum=301 src=10.10.10.1 dst=10.10.10.3
+ data (3) = el!
+ Dev::receive(msgtype/mac = 168430083, size = 22)
+ IPIn::recv: IPHeader: version= total_len=22 id=56121 DF=0 MF=0 offset=18 TTL=64 proto=0 checksum=62 src=10.10.10.1 dst=10.10.10.3
+ data (2) = !!
+ IPIn::recv: Paquete completo: data = 'adios mundo cruel!!!'
+ Recibido 'adios mundo cruel!!!' (len 20) de 10.10.10.1 para 10.10.10.3 (proto = 0)
+
+El proceso test_ipout de este host no tiene relevancia.
+
+
+.. vim: filetype=rst :
--- /dev/null
+10.10.10.1 0.0.0.0 0
+10.10.10.2 0.0.0.0 0
+10.10.10.3 10.10.10.5 0
--- /dev/null
+10.10.10.1 0.0.0.0 0
+10.10.10.2 0.0.0.0 0
+10.10.10.3 10.10.10.5 0
+10.10.10.5 0.0.0.0 0
--- /dev/null
+10.10.10.1 0.0.0.0 0
+10.10.10.2 0.0.0.0 0
+10.10.10.3 10.10.10.5 0
+10.10.10.5 0.0.0.0 0
--- /dev/null
+10.10.10.1 10.10.10.5 0
+10.10.10.2 10.10.10.5 0
+10.10.10.3 0.0.0.0 0
+10.10.10.5 0.0.0.0 0
--- /dev/null
+10.10.10.1 0.0.0.0 0
+10.10.10.2 0.0.0.0 0
+10.10.10.3 0.0.0.0 0
+10.10.10.5 0.0.0.0 0
--- /dev/null
+# Makefile de ejemplo para C++
+#
+# Creado: jue abr 15 15:34:19 ART 2004
+#
+# Copyleft 2004 - Leandro Lucarella, Bajo licencia GPL [http://www.gnu.org/]
+#
+
+# CONFIGURACION
+################
+
+# Opciones para el compilador C/C++ en modo ansi.
+CFLAGS = -Wall -ansi -pedantic-errors
+
+# Para que explote lo mas posible
+#CFLAGS += -O3 -DNDEBUG
+
+# Para valgrind o debug
+CFLAGS += -ggdb -DDEBUG
+
+# Opciones para el compilador C++.
+CXXFLAGS = $(CFLAGS) -fno-inline
+
+# Opciones del enlazador.
+#LDFLAGS=
+
+# Compilador.
+CC=g++
+
+# Programas
+targets=test_send test_recv test_ipaddr test_ipin test_ipout
+
+# Fuentes
+fuentes ?= $(wildcard *.cpp)
+
+
+# REGLAS
+#########
+
+.PHONY: all clean
+
+all: depend $(targets)
+
+test_send: test_send.o dev.o
+
+test_recv: test_recv.o dev.o
+
+test_ipaddr: test_ipaddr.o ipaddr.o ipheader.o
+
+test_ipin: test_ipin.o ipin.o ipaddr.o ipheader.o dev.o
+
+test_ipout: test_ipout.o ipout.o ipaddr.o ipheader.o dev.o routetable.o
+
+depend:
+ @makedepend $(fuentes) > /dev/null 2>&1
+
+clean:
+ @$(RM) -fv *.o Makefile.bak $(targets)
+
+# DO NOT DELETE
+
+dev.o: dev.h /usr/include/unistd.h /usr/include/features.h
+dev.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+dev.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+dev.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h
+dev.o: /usr/include/bits/confname.h /usr/include/getopt.h
+dev.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+dev.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h
+dev.o: /usr/include/bits/endian.h /usr/include/sys/select.h
+dev.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+dev.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h
+dev.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+dev.o: /usr/include/sys/ipc.h /usr/include/bits/ipctypes.h
+dev.o: /usr/include/bits/ipc.h /usr/include/sys/msg.h /usr/include/bits/msq.h
+ipaddr.o: ipaddr.h
+ipheader.o: ipheader.h ipaddr.h /usr/include/stdint.h /usr/include/features.h
+ipheader.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+ipheader.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+ipin.o: ipin.h ipaddr.h ipheader.h /usr/include/stdint.h
+ipin.o: /usr/include/features.h /usr/include/sys/cdefs.h
+ipin.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+ipin.o: /usr/include/bits/wordsize.h dev.h
+ipout.o: ipout.h ipaddr.h ipheader.h /usr/include/stdint.h
+ipout.o: /usr/include/features.h /usr/include/sys/cdefs.h
+ipout.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+ipout.o: /usr/include/bits/wordsize.h routetable.h dev.h
+ipout.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+ipout.o: /usr/include/bits/types.h /usr/include/bits/typesizes.h
+ipout.o: /usr/include/bits/confname.h /usr/include/getopt.h
+ipout.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+ipout.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h
+ipout.o: /usr/include/bits/endian.h /usr/include/sys/select.h
+ipout.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+ipout.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h
+ipout.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+ipout.o: /usr/include/sys/ipc.h /usr/include/bits/ipctypes.h
+ipout.o: /usr/include/bits/ipc.h /usr/include/sys/msg.h
+ipout.o: /usr/include/bits/msq.h
+routetable.o: routetable.h dev.h ipaddr.h
+test_ipaddr.o: ipaddr.h ipheader.h /usr/include/stdint.h
+test_ipaddr.o: /usr/include/features.h /usr/include/sys/cdefs.h
+test_ipaddr.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+test_ipaddr.o: /usr/include/bits/wordsize.h
+test_ipin.o: ipin.h ipaddr.h ipheader.h /usr/include/stdint.h
+test_ipin.o: /usr/include/features.h /usr/include/sys/cdefs.h
+test_ipin.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+test_ipin.o: /usr/include/bits/wordsize.h dev.h /usr/include/unistd.h
+test_ipin.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+test_ipin.o: /usr/include/bits/typesizes.h /usr/include/bits/confname.h
+test_ipin.o: /usr/include/getopt.h /usr/include/fcntl.h
+test_ipin.o: /usr/include/bits/fcntl.h /usr/include/sys/types.h
+test_ipin.o: /usr/include/time.h /usr/include/endian.h
+test_ipin.o: /usr/include/bits/endian.h /usr/include/sys/select.h
+test_ipin.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+test_ipin.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h
+test_ipin.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+test_ipin.o: /usr/include/sys/ipc.h /usr/include/bits/ipctypes.h
+test_ipin.o: /usr/include/bits/ipc.h /usr/include/sys/msg.h
+test_ipin.o: /usr/include/bits/msq.h
+test_ipout.o: ipout.h ipaddr.h ipheader.h /usr/include/stdint.h
+test_ipout.o: /usr/include/features.h /usr/include/sys/cdefs.h
+test_ipout.o: /usr/include/gnu/stubs.h /usr/include/bits/wchar.h
+test_ipout.o: /usr/include/bits/wordsize.h routetable.h dev.h
+test_ipout.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+test_ipout.o: /usr/include/bits/types.h /usr/include/bits/typesizes.h
+test_ipout.o: /usr/include/bits/confname.h /usr/include/getopt.h
+test_ipout.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+test_ipout.o: /usr/include/sys/types.h /usr/include/time.h
+test_ipout.o: /usr/include/endian.h /usr/include/bits/endian.h
+test_ipout.o: /usr/include/sys/select.h /usr/include/bits/select.h
+test_ipout.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+test_ipout.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+test_ipout.o: /usr/include/bits/sched.h /usr/include/sys/ipc.h
+test_ipout.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+test_ipout.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
+test_recv.o: dev.h /usr/include/unistd.h /usr/include/features.h
+test_recv.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+test_recv.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+test_recv.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h
+test_recv.o: /usr/include/bits/confname.h /usr/include/getopt.h
+test_recv.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+test_recv.o: /usr/include/sys/types.h /usr/include/time.h
+test_recv.o: /usr/include/endian.h /usr/include/bits/endian.h
+test_recv.o: /usr/include/sys/select.h /usr/include/bits/select.h
+test_recv.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+test_recv.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+test_recv.o: /usr/include/bits/sched.h /usr/include/sys/ipc.h
+test_recv.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+test_recv.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
+test_send.o: dev.h /usr/include/unistd.h /usr/include/features.h
+test_send.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+test_send.o: /usr/include/bits/posix_opt.h /usr/include/bits/types.h
+test_send.o: /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h
+test_send.o: /usr/include/bits/confname.h /usr/include/getopt.h
+test_send.o: /usr/include/fcntl.h /usr/include/bits/fcntl.h
+test_send.o: /usr/include/sys/types.h /usr/include/time.h
+test_send.o: /usr/include/endian.h /usr/include/bits/endian.h
+test_send.o: /usr/include/sys/select.h /usr/include/bits/select.h
+test_send.o: /usr/include/bits/sigset.h /usr/include/bits/time.h
+test_send.o: /usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h
+test_send.o: /usr/include/bits/sched.h /usr/include/sys/ipc.h
+test_send.o: /usr/include/bits/ipctypes.h /usr/include/bits/ipc.h
+test_send.o: /usr/include/sys/msg.h /usr/include/bits/msq.h
--- /dev/null
+#include "dev.h"
+#include <cstring>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+struct Frame
+{
+ Dev::mac_type mac;
+ size_t size;
+ char frame[1];
+};
+
+Dev::Dev(mac_type mac, size_t mtu, key_t key)
+ throw (std::runtime_error, std::logic_error):
+ mac(mac), mtu(mtu)
+{
+ if (mtu > DEV_MAX_MTU)
+ throw std::logic_error("MTU más grande que DEV_MAX_MTU");
+ que_id = msgget(key, 0666); // Debe estar previamente creada
+ if (que_id == -1)
+ throw std::runtime_error("No se pudo crear la cola");
+}
+
+void Dev::transmit(const std::string& data, const mac_type& mac)
+ throw (std::runtime_error, std::logic_error)
+{
+ if (data.size() > mtu)
+ throw std::logic_error("Tamaño de datos mayor al MTU");
+ Frame* f = (Frame*) malloc(sizeof(Frame) + mtu);
+ if (!f)
+ throw std::runtime_error("No se puede reservar memoria");
+ f->mac = mac;
+ f->size = data.size();
+ memcpy(f->frame, data.c_str(), data.size());
+ int res = msgsnd(que_id, f, mtu + sizeof(size_t), 0);
+#ifdef DEBUG
+ std::cout << "Dev::transmit(msgtype/mac = " << f->mac << ", size = "
+ << f->size << ")\n";
+#endif
+ free(f);
+ if (res == -1)
+ throw std::runtime_error("Error al poner en la cola");
+}
+
+std::string Dev::receive() throw (std::runtime_error)
+{
+ Frame* f = (Frame*) malloc(sizeof(Frame) + mtu);
+ if (!f)
+ throw std::runtime_error("No se puede reservar memoria");
+ int res = msgrcv(que_id, f, mtu + sizeof(size_t), mac, 0);
+ if (res == -1)
+ {
+ free(f);
+ throw std::runtime_error("Error al sacar de la cola");
+ }
+ std::string s((char*) f->frame, f->size);
+ free(f);
+#ifdef DEBUG
+ std::cout << "Dev::receive(msgtype/mac = " << mac << ", size = "
+ << s.size() << ")\n";
+#endif
+ return s;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _DEV_H_
+#define _DEV_H_
+
+#include <stdexcept>
+
+#define DEV_DEFAULT_KEY 0x1abcdef1
+#define DEV_MAX_MTU 1500
+
+/// Dispositivo de red (capa de enlace)
+struct Dev
+{
+
+ /// Tipo de la mac
+ typedef long mac_type;
+
+ /// Dirección MAC
+ mac_type mac;
+
+ /// MTU
+ size_t mtu;
+
+ /// Identificador de la cola a usar
+ int que_id;
+
+ /// Constructor
+ Dev(mac_type mac, size_t mtu = DEV_MAX_MTU, key_t key = DEV_DEFAULT_KEY)
+ throw (std::runtime_error, std::logic_error);
+
+ /// Envía un frame
+ void transmit(const std::string& data, const mac_type& mac)
+ throw (std::runtime_error, std::logic_error);
+
+ /// Recibe un frame
+ std::string receive()
+ throw (std::runtime_error);
+
+ // Nada de andar copiando placas...
+ private:
+ Dev(const Dev&);
+ Dev& operator=(const Dev&);
+
+};
+
+#endif // _DEV_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipaddr.h"
+#include <sstream>
+
+/// Constructor
+IPAddr::IPAddr()
+{
+ atoms[0] = 0;
+ atoms[1] = 0;
+ atoms[2] = 0;
+ atoms[3] = 0;
+}
+
+/// Constructor
+IPAddr::IPAddr(atom a1, atom a2, atom a3, atom a4)
+{
+ atoms[0] = a1;
+ atoms[1] = a2;
+ atoms[2] = a3;
+ atoms[3] = a4;
+}
+
+/// Constructor
+IPAddr::IPAddr(int ip)
+{
+ atoms[0] = ip >> 24;
+ atoms[1] = ip >> 16;
+ atoms[2] = ip >> 8;
+ atoms[3] = ip;
+}
+
+/// Constructor
+IPAddr::IPAddr(const char* ip) throw (std::logic_error)
+{
+ std::istringstream iss(ip);
+ std::string ips;
+ for (int i = 0; i < 4; ++i)
+ {
+ if (!std::getline(iss, ips, '.'))
+ throw std::logic_error("Dirección IP inválida");
+ atoms[i] = std::atoi(ips.c_str());
+ }
+}
+
+/// Constructor
+//IPAddr::IPAddr(const std::string& ip) throw (std::logic_error)
+//{
+// IPAddr(ip.c_str());
+//}
+
+/// Operador de casteo a unsigned
+IPAddr::operator unsigned () const
+{
+ return (atoms[0] << 24) + (atoms[1] << 16) + (atoms[2] << 8) + atoms[3];
+}
+
+/// Operador de casteo a std::string
+IPAddr::operator std::string () const
+{
+ std::ostringstream oss;
+ oss << unsigned(atoms[0]) << "." << unsigned(atoms[1]) << "."
+ << unsigned(atoms[2]) << "." << unsigned(atoms[3]);
+ return oss.str();
+}
+
+std::ostream& operator<< (std::ostream& os, const IPAddr& ip)
+{
+ return os << std::string(ip);
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPADDR_H_
+#define _IPADDR_H_
+
+#include <string>
+#include <cstdlib>
+#include <stdexcept>
+
+/// Dirección IP
+struct IPAddr
+{
+
+ /// Átomo de dirección IP
+ typedef unsigned char atom;
+
+ /// Representación interna
+ atom atoms[4];
+
+ /// Constructor
+ IPAddr();
+
+ /// Constructor
+ IPAddr(atom a1, atom a2, atom a3, atom a4);
+
+ /// Constructor
+ IPAddr(int ip);
+
+ /// Constructor
+ IPAddr(const char* ip) throw (std::logic_error);
+
+ /// Constructor
+ //IPAddr(const std::string& ip) throw (std::logic_error);
+
+ /// Operador de casteo a unsigned
+ operator unsigned () const;
+
+ /// Operador de casteo a std::string
+ operator std::string () const;
+
+};
+
+std::ostream& operator<< (std::ostream& os, const IPAddr& ip);
+
+#endif // _IPADDR_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#include "ipheader.h"
+
+IPHeader::IPHeader(uint8_t version, uint16_t total_len, uint16_t id, bool df,
+ bool mf, uint16_t offset, uint8_t ttl, uint8_t proto,
+ const IPAddr& src, const IPAddr& dst):
+ version(version), total_len(total_len), id(id), reserved_flag(0),
+ df(df), mf(mf), offset(offset), ttl(ttl), proto(proto), checksum(0),
+ src(src), dst(dst)
+{
+ do_checksum();
+}
+
+IPHeader::IPHeader(const std::string& s)
+{
+ *this = *((IPHeader*)s.c_str());
+}
+
+size_t IPHeader::header_len()
+{
+ return sizeof(IPHeader);
+}
+
+bool IPHeader::check_checksum() const
+{
+ IPHeader iph = *this;
+ iph.checksum = 0;
+ char* raw = (char*) &iph;
+ uint16_t sum = 0;
+ for (unsigned i = 0; i < sizeof(IPHeader); ++i)
+ sum += raw[i];
+ return sum == checksum;
+}
+
+void IPHeader::do_checksum()
+{
+ checksum = 0;
+ char* raw = (char*) this;
+ uint16_t sum = 0;
+ for (unsigned i = 0; i < sizeof(IPHeader); ++i)
+ sum += raw[i];
+ checksum = sum;
+}
+
+std::ostream& operator<<(std::ostream& os, const IPHeader& iph)
+{
+ return os
+ << "version=" << iph.version
+ << " total_len=" << iph.total_len
+ << " id=" << iph.id
+ << " DF=" << bool(iph.df)
+ << " MF=" << bool(iph.mf)
+ << " offset=" << unsigned(iph.offset)
+ << " TTL=" << unsigned(iph.ttl)
+ << " proto=" << unsigned(iph.proto)
+ << " checksum=" << iph.checksum
+ << " src=" << IPAddr(iph.src)
+ << " dst=" << IPAddr(iph.dst);
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPHEADER_H_
+#define _IPHEADER_H_
+
+#include "ipaddr.h"
+#include <string>
+#include <ostream>
+#include <stdint.h>
+
+/// Dispositivo de red (capa de enlace)
+struct IPHeader
+{
+
+ // Campos
+ uint8_t version;
+ //TODO IHL
+ //TODO TOS
+ uint16_t total_len;
+ uint16_t id;
+ uint16_t reserved_flag: 1;
+ uint16_t df: 1;
+ uint16_t mf: 1;
+ uint16_t offset: 13;
+ uint8_t ttl;
+ uint8_t proto;
+ uint16_t checksum;
+ uint32_t src;
+ uint32_t dst;
+
+ IPHeader(uint8_t version, uint16_t total_len, uint16_t id, bool df,
+ bool mf, uint16_t offset, uint8_t ttl, uint8_t proto,
+ const IPAddr& src, const IPAddr& dst);
+
+ IPHeader(const std::string& s);
+
+ static size_t header_len();
+
+ bool check_checksum() const;
+
+ void do_checksum();
+
+};
+
+std::ostream& operator<<(std::ostream& os, const IPHeader& iph);
+
+#endif // _IPHEADER_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipin.h"
+#include "ipheader.h"
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+/// Constructor
+IPIn::IPIn(const IPAddr& ip, Dev& dev, Dev& forward_que, bool router,
+ bool forward, std::ostream& log):
+ ip(ip), dev(dev), forward_que(forward_que), router(router),
+ forward(forward), log(log)
+{
+ if (router) forward = true;
+}
+
+void IPIn::drop(const std::string& msg, const std::string& buf)
+{
+ log << "IPIn::drop (" << ip << "): " << msg << "\n\tBuffer: " << buf
+ << "\n";
+}
+
+void IPIn::drop(const std::string& msg, const IPHeader& iph)
+{
+ log << "IPIn::drop (" << ip << "): " << msg << "\n\tIPHeader: " << iph
+ << "\n";
+}
+
+/// Recibe un paquete IP
+std::string IPIn::recv(uint8_t proto, IPAddr& src, IPAddr& dst) throw (std::runtime_error)
+{
+ while (true)
+ {
+ std::string buf = dev.receive();
+ // No es siquiera IP
+ if (buf.size() < IPHeader::header_len())
+ {
+ drop("Cabecera incompleta o no es IP", buf);
+ continue;
+ }
+ IPHeader iph(buf);
+#ifdef DEBUG
+ std::cout << "IPIn::recv (" << ip << "): IPHeader: " << iph << "\n";
+ std::string tmp = buf.substr(iph.header_len());
+ std::cout << "\tdata (" << tmp.size() << ") = " << tmp << "\n";
+#endif
+ if (iph.version != 4)
+ {
+ drop("Versión IP incorrecta", iph);
+ continue;
+ }
+ if (!iph.check_checksum())
+ {
+ drop("Mal checksum", iph);
+ continue;
+ }
+ // No es para nosotros y no forwardeamos
+ if (iph.dst != ip && !forward)
+ {
+ drop("No es para nosotros y no hacemos forward", iph);
+ continue;
+ }
+ // No es para nosotros pero forwardeamos
+ else if (iph.dst != ip)
+ {
+ forward_que.transmit(buf, ip);
+ continue;
+ }
+ // Es para nosotros pero somos router
+ else if (router)
+ {
+ drop("Es para nosotros pero somos un router", iph);
+ continue;
+ }
+ // Es para nosotros y somos un host
+ // Guarda en buffer
+ buffer[iph][iph.offset] = buf.substr(iph.header_len());
+ // Si tiene más fragmentos o es un protocolo distinto, sigo
+ if (iph.mf || (iph.proto != proto))
+ continue;
+ // No hay más fragmentos, reensamblamos (de ser necesario)
+ std::string data;
+ for (offsetmap_type::iterator i = buffer[iph].begin();
+ i != buffer[iph].end(); ++i)
+ {
+ //TODO chequear que los fragmentos estén todos
+ data += i->second;
+ }
+#ifdef DEBUG
+ std::cout << "IPIn::recv (" << ip << "): Paquete completo: data = '"
+ << data << "'\n";
+#endif
+ buffer.erase(iph);
+ //TODO faltaría limpiar fragmentos viejos cada tanto (timer?)
+ src = iph.src;
+ dst = iph.dst;
+ return data;
+ }
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPIN_H_
+#define _IPIN_H_
+
+#include "ipaddr.h"
+#include "ipheader.h"
+#include "dev.h"
+#include <map>
+#include <string>
+#include <iostream>
+#include <stdexcept>
+
+/// IP de recepción
+struct IPIn
+{
+
+ /// Dirección IP
+ IPAddr ip;
+
+ /// Dispositivo de red
+ Dev& dev;
+
+ /// Cola para forwardear paquetes
+ Dev& forward_que;
+
+ /// Indica si es un router
+ bool router;
+
+ /// Indica si hace forwarding
+ bool forward;
+
+ /// Dispositivo de logging
+ std::ostream& log;
+
+ /// Buffers de recepción
+ struct BufferKey
+ {
+ uint16_t id;
+ uint32_t src, dst;
+ uint8_t proto;
+ BufferKey(const IPHeader& h):
+ id(h.id), src(h.src), dst(h.dst), proto(h.proto)
+ {}
+ bool operator< (const BufferKey& b) const
+ { return id < b.id && src < b.src && dst < b.dst && proto < b.proto; }
+ };
+ typedef std::map< uint16_t, std::string > offsetmap_type;
+ typedef std::map< BufferKey, offsetmap_type > buffer_type;
+ buffer_type buffer;
+
+ /// Constructor
+ IPIn(const IPAddr& ip, Dev& dev, Dev& forward_que, bool router = false,
+ bool forward = false, std::ostream& log = std::cout);
+
+ /// Descarta un paquete
+ void drop(const std::string& msg, const std::string& buf);
+ void drop(const std::string& msg, const IPHeader& iph);
+
+ /// Recibe un paquete IP
+ std::string recv(uint8_t proto, IPAddr& src, IPAddr& dst)
+ throw (std::runtime_error);
+
+ // Nada de andar copiando placas...
+ private:
+ IPIn(const IPIn&);
+ IPIn& operator=(const IPIn&);
+
+};
+
+#endif // _IPIN_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipout.h"
+#include "ipheader.h"
+#include <ctime>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+/// Constructor
+IPOut::IPOut(const IPAddr& ip, RouteTable& rtable, Dev& forward_que, std::ostream& log):
+ ip(ip), rtable(rtable), forward_que(forward_que), log(log)
+{
+}
+
+void IPOut::drop(const std::string& msg, const std::string& buf)
+{
+ log << "IPOut::drop (" << ip << "): " << msg << "\n\tBuffer: " << buf
+ << "\n";
+}
+
+void IPOut::drop(const std::string& msg, const IPHeader& iph)
+{
+ log << "IPOut::drop (" << ip << "): " << msg << "\n\tIPHeader: " << iph
+ << "\n";
+}
+
+/// Envía un paquete IP
+bool IPOut::send(const std::string& data, uint8_t proto, IPAddr dst, IPAddr src,
+ bool df, uint8_t ttl, uint16_t id)
+ throw (std::runtime_error)
+{
+ // Mando todo lo que tengo para forwardear
+ while (to_forward())
+ {
+ std::string buf = forward_que.receive();
+ IPHeader iph(buf);
+#ifdef DEBUG
+ std::cout << "IPOut::send (" << ip << "): A forwardear\n";
+#endif
+ send(iph, buf.substr(iph.header_len()));
+ }
+ // Mando el paquete en sí
+ // Armamos cabecera
+ if (!src)
+ src = ip;
+ if (!id)
+ id = get_id();
+ IPHeader iph(4, IPHeader::header_len() + data.size(), id, df, 0, 0,
+ ttl, proto, src, dst);
+ return send(iph, data);
+}
+
+/// Envía un paquete IP
+bool IPOut::send(IPHeader iph, std::string data) throw (std::runtime_error)
+{
+ // Buscamos ruta
+ RouteTable::Route* r = rtable.get(iph.dst);
+ if (!r)
+ {
+ drop("No existe una ruta para el destino", iph);
+ return false;
+ }
+ // No quieren fragmentar
+ if (iph.df && (IPHeader::header_len() + data.size() > r->iface->mtu))
+ {
+ drop("Tamaño de paquete más grande que MTU y DF=1", iph);
+ return false;
+ }
+ // Fragmenta (de ser necesario)
+ int max_payload = r->iface->mtu - IPHeader::header_len();
+ int cant_frag = data.size() / max_payload;
+ if (data.size() % max_payload)
+ ++cant_frag;
+ for (int i = 0; i < cant_frag; ++i)
+ {
+ IPHeader iph2 = iph;
+ if (i != (cant_frag - 1))
+ iph2.mf = 1;
+ iph2.offset += i * max_payload;
+ iph2.total_len -= i * max_payload;
+ iph2.do_checksum();
+ std::string buf((char*) &iph2, sizeof(IPHeader));
+ buf += data.substr(i * max_payload, max_payload);
+#ifdef DEBUG
+ std::cout << "IPOut::send (" << ip << "): Fragmento " << i
+ << " => IPHeader: " << iph2 << "\n";
+ std::string tmp = data.substr(i * max_payload, max_payload);
+ std::cout << "\tdata (" << tmp.size() << ") = " << tmp << "\n";
+#endif
+ r->iface->transmit(buf, r->gateway ? r->gateway : IPAddr(iph.dst));
+ }
+ return true;
+}
+
+/// Obtiene un identificador para el paquete
+uint16_t IPOut::get_id() const
+{
+ return time(NULL);
+}
+
+/// Se fija si hay paquetes a forwardear (y devuelve cuantos hay)
+unsigned IPOut::to_forward()
+{
+ struct msqid_ds minfo;
+ msgctl(forward_que.que_id, IPC_STAT, &minfo);
+ return minfo.msg_qnum;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _IPOUT_H_
+#define _IPOUT_H_
+
+#include "ipaddr.h"
+#include "ipheader.h"
+#include "routetable.h"
+#include <iostream>
+#include <string>
+#include <stdexcept>
+
+/// IP de envío
+struct IPOut
+{
+
+ /// Dirección MAC
+ IPAddr ip;
+
+ /// Dispositivo de logging
+ RouteTable& rtable;
+
+ /// Cola para forwardear paquetes
+ Dev& forward_que;
+
+ /// Dispositivo de logging
+ std::ostream& log;
+
+ /// Constructor
+ IPOut(const IPAddr& ip, RouteTable& rtable, Dev& forward_que,
+ std::ostream& log = std::cout);
+
+ /// Descarta un paquete
+ void drop(const std::string& msg, const std::string& buf);
+ void drop(const std::string& msg, const IPHeader& iph);
+
+ /// Envía un paquete IP a armar (y forwardea los encolados, de haber)
+ bool send(const std::string& data, uint8_t proto, IPAddr dst,
+ IPAddr src = 0, bool df = 0, uint8_t ttl = 64, uint16_t id = 0)
+ throw (std::runtime_error);
+
+ /// Envía un paquete IP ya armado
+ bool send(IPHeader iph, std::string data) throw (std::runtime_error);
+
+ /// Obtiene un identificador para el paquete
+ uint16_t get_id() const;
+
+ /// Se fija si hay paquetes a forwardear (y devuelve cuantos hay)
+ unsigned to_forward();
+
+ // Nada de andar copiando...
+ private:
+ IPOut(const IPOut&);
+ IPOut& operator=(const IPOut&);
+
+};
+
+#endif // _IPOUT_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#include "routetable.h"
+#ifdef DEBUG
+#include <iostream>
+#endif
+
+RouteTable::RouteTable(Dev& default_iface): default_iface(default_iface)
+{
+}
+
+void RouteTable::add(const IPAddr& net, const IPAddr& gw, unsigned metric, Dev& iface)
+{
+ table[net] = Route(gw, metric, iface);
+#ifdef DEBUG
+ std::cout << "Se agregó tabla para " << net << ": gw = " << gw
+ << ", metric = " << metric << "\n";
+#endif
+}
+
+void RouteTable::del(const IPAddr& net)
+{
+ table.erase(net);
+}
+
+RouteTable::Route* RouteTable::get(const IPAddr& dst)
+{
+ // No existe
+ if (table.find(dst) == table.end())
+ return 0;
+ return &table[dst];
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#ifndef _ROUTETABLE_H_
+#define _ROUTETABLE_H_
+
+#include "dev.h"
+#include "ipaddr.h"
+#include <map>
+
+/// Tabla de ruteo
+struct RouteTable
+{
+
+ /// Ruta
+ struct Route
+ {
+ IPAddr gateway;
+ unsigned metric;
+ Dev* iface;
+ Route(): gateway(0), metric(0), iface(0) {}
+ Route(const IPAddr& gateway, unsigned metric, Dev& iface):
+ gateway(gateway), metric(metric), iface(&iface) {}
+ };
+
+ /// Tabla
+ std::map< IPAddr, Route > table;
+
+ /// Interfaz por default
+ Dev& default_iface;
+
+ /// Constructor
+ RouteTable(Dev& default_iface);
+
+ /// Agrega ruta
+ void add(const IPAddr& net, const IPAddr& gw, unsigned metric, Dev& iface);
+
+ /// Borra ruta
+ void del(const IPAddr& net);
+
+ /// Obtiene dirección e interfaz por la cual salir para un destino
+ Route* get(const IPAddr& dst);
+
+};
+
+#endif // _ROUTETABLE_H_
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+#!/bin/sh
+
+# Host 10.10.10.1
+./test_ipin 10.10.10.5 25 1 1 &
+echo 'Adios mundo cruel!!!' | ./test_ipout 10.10.10.1 10.10.10.3 25 \
+ ../rutas_ejemplo/route_10.10.10.1.txt
+
+# Router 10.10.10.5
+./test_ipin 10.10.10.5 25 1 1 &
+echo | ./test_ipout 10.10.10.5 10.10.10.1 23 \
+ ../rutas_ejemplo/route_10.10.10.5.txt
+
+# Host 10.10.10.3
+./test_ipin 10.10.10.3 23 0 0 &
+echo | ./test_ipout 10.10.10.3 10.10.10.5 25 \
+ ../rutas_ejemplo/route_10.10.10.3.txt
+
+# Limpio
+sleep 1
+killall test_ipin
+ipcrm -Q 0x1abcdef1
+ipcrm -Q 0x1abcdef2
--- /dev/null
+#include "ipaddr.h"
+#include "ipheader.h"
+#include <iostream>
+
+int main()
+{
+ // Addr
+ IPAddr ip1(0x0a0a0a05);
+ IPAddr ip2("10.10.10.1");
+ IPAddr ip3(10, 10, 10, 2);
+ std::cout << "IP1 = " << ip1 << "\n";
+ std::cout << "IP2 = " << ip2 << "\n";
+ std::cout << "IP3 = " << ip3 << "\n";
+ // Header
+ IPHeader h1(4, 20, 1, 1, 0, 0, 64, 0x11, ip1, ip2);
+ std::cout << "Header1 = " << h1 << "\n";
+ if (h1.check_checksum())
+ std::cout << "Checksum OK\n";
+ else
+ std::cout << "Checksum MAL!\n";
+ h1.checksum = 1;
+ if (h1.check_checksum())
+ std::cout << "Checksum OK\n";
+ else
+ std::cout << "Checksum MAL!\n";
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipin.h"
+#include "ipaddr.h"
+#include "dev.h"
+#include <iostream>
+#include <cstdlib>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+// Uso: ./test_ipin ip mtu router forward proto queue_id
+
+int main(int argc, char* argv[])
+{
+ IPAddr addr("10.10.10.1");
+ unsigned mtu = 25;
+ bool router = false;
+ bool forward = false;
+ unsigned proto = 0;
+ key_t queue_id = DEV_DEFAULT_KEY;
+ if (argc > 1)
+ addr = IPAddr(argv[1]);
+ if (argc > 2)
+ mtu = atoi(argv[2]);
+ if (argc > 3)
+ router = atoi(argv[3]);
+ if (argc > 4)
+ forward = atoi(argv[4]);
+ if (argc > 5)
+ proto = atoi(argv[5]);
+ if (argc > 6)
+ queue_id = atoi(argv[6]);
+ int que_id = msgget(queue_id, IPC_CREAT | 0666);
+ assert(que_id != -1);
+ Dev dev(addr, mtu, queue_id);
+ que_id = msgget(queue_id+1, IPC_CREAT | 0666);
+ assert(que_id != -1);
+ Dev fwque(addr, DEV_MAX_MTU, queue_id+1);
+ IPIn ipin(addr, dev, fwque, router, forward, std::cerr);
+ struct msqid_ds minfo;
+ for (msgctl(dev.que_id, IPC_STAT, &minfo); minfo.msg_qnum;
+ msgctl(dev.que_id, IPC_STAT, &minfo))
+ {
+ IPAddr src, dst;
+ std::cout << "Quedan " << minfo.msg_qnum << " mensajes en la cola\n";
+ std::string s = ipin.recv(proto, src, dst);
+ std::cout << "Recibido '" << s << "' (len " << s.size() << ") de "
+ << src << " para " << dst << " (proto = " << proto << ")\n";
+ }
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "ipout.h"
+#include "ipaddr.h"
+#include "routetable.h"
+#include "dev.h"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+// Uso: ./test_ipout ip dst mtu routes_file proto queue_id
+
+void add_routes(RouteTable& rt, std::istream& is, Dev& dev);
+
+int main(int argc, char* argv[])
+{
+ IPAddr addr("10.10.10.2");
+ IPAddr dst("10.10.10.1");
+ unsigned mtu = 25;
+ unsigned proto = 0;
+ key_t queue_id = DEV_DEFAULT_KEY;
+ std::string fname = "route.txt";
+ if (argc > 1)
+ addr = IPAddr(argv[1]);
+ if (argc > 2)
+ dst = IPAddr(argv[2]);
+ if (argc > 3)
+ mtu = atoi(argv[3]);
+ if (argc > 4)
+ fname = argv[4];
+ if (argc > 5)
+ proto = atoi(argv[5]);
+ if (argc > 6)
+ queue_id = atoi(argv[6]);
+ int que_id = msgget(queue_id, IPC_CREAT | 0666); assert(que_id != -1);
+ que_id = msgget(queue_id+1, IPC_CREAT | 0666); assert(que_id != -1);
+ std::ifstream ifs(fname.c_str()); assert(ifs);
+ Dev dev(addr, mtu, queue_id);
+ Dev fwque(addr, DEV_MAX_MTU, queue_id+1);
+ RouteTable table(dev);
+ add_routes(table, ifs, dev);
+ IPOut ipout(addr, table, fwque, std::cerr);
+ std::string msg;
+ while (std::getline(std::cin, msg))
+ {
+ if (ipout.send(msg, proto, dst))
+ std::cout << "Enviado '" << msg << "' a " << dst << "\n";
+ else
+ std::cout << "NO SE PUDO ENVIAR '" << msg << "' a " << dst << "\n";
+ }
+ return 0;
+}
+
+void add_routes(RouteTable& rt, std::istream& is, Dev& dev)
+{
+ std::string line;
+ while (std::getline(is, line))
+ {
+ std::istringstream iss(line);
+ std::string net;
+ std::string gw;
+ unsigned metric;
+ iss >> net >> gw >> metric;
+ if (net == "0") net = "0.0.0.0";
+ if (gw == "0") gw = "0.0.0.0";
+ rt.add(net.c_str(), gw.c_str(), metric, dev);
+ }
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "dev.h"
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+int main()
+{
+ Dev dev(4321);
+ struct msqid_ds minfo;
+ for (msgctl(dev.que_id, IPC_STAT, &minfo); minfo.msg_qnum;
+ msgctl(dev.que_id, IPC_STAT, &minfo))
+ {
+ std::cout << "Quedan " << minfo.msg_qnum << " mensajes en la cola\n";
+ std::string s = dev.receive();
+ std::cout << "Recibido '" << s << "' (len " << s.size() << ")\n";
+ }
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :
--- /dev/null
+
+#include "dev.h"
+#include <iostream>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+
+int main()
+{
+ int que_id = msgget(DEV_DEFAULT_KEY, IPC_CREAT | 0666);
+ assert(que_id != -1);
+ Dev dev(1234);
+ dev.transmit("hola mundo", 4321);
+ std::cout << "Enviado 'hola mundo' a 4321\n";
+ return 0;
+}
+
+// vim: set et sw=4 sts=4 :