08 julio, 2008

Enrutar sobre varias redes idénticas

Después de varios días he conseguido echar a correr una curiosa configuración de red. La idea es del sistema es simular con VMWare una red de sucursales. Cada sucursal tiene una red privada y se conectan a la red general a traves de una única ip. El tráfico debe poder hacerse bidireccionalmente.

El problema surge al tener dentro de la máquina que virtualiza, N redes idénticas ya que cuando llega un paquete no sabes hacian dónde dirigirlo. La gracia es que a cada una de esas redes, se le asigna una ip externa. Con lo que todo lo que hay que hacer es conectar esa ip externa con su respectiva red interna. Con VMWare queda un script parecido a lo siguiente:
#Levantamos una interfaz virtual con la ip publica
ifconfig eth1:$SUCURSAL $IP netmask 255.255.255.0

#Creamos el «switch» de VMWare para esa sucursal
mknod /dev/vmnet$SUCURSAL c 119 $SUCURSAL

#Creamos el interfaz de red virtual de VMWare que estará conectado al Switch y que será el gateway de las maquina de esa red interna
/usr/bin/vmnet-netifup -d /var/run/vmnet-netifup-vmnet$SUCURSAL.pid /dev/vmnet$SUCURSAL vmnet$SUCURSAL

#Configuramos esa interfaz virtual con la ip del router, que sera la misma para todas las redes.
ifconfig vmnet$SUCURSAL $ROUTER netmask 255.255.255.0

#Redirigimos los puertos que deseemos del router hacia las maquinas que queramos que haya dentro de la red interna, con algo de la forma...
iptables -A PREROUTING -t nat -d $IP -p tcp --dport $ORIG -j DNAT --to $DEST

El problema es que con esa redirección es que como todas las redes son iguales no podemos saber hacia cuál de todas las subredes redirigir dichos paquetes. Aquí es donde entra en juego la maravilla del marcado de los paquetes. Ya que a partir de la version 1.4 de iptables, se ha eliminado el «target» ROUTE que habría simplificado todo bastante.

La idea es marcar con iptables los paquetes que vayan a cada ip externa de cada sucursal con una marca distinta, por ejemplo el mismo codigo de sucursal.
iptables -A PREROUTING -t mangle -d $IP -i ! vmnet$SUCURSAL -j MARK --set-mark $SUCURSAL

Ahora con iproute2 le decimos al kernel que agarre los paquetes con esa marca y los enchufe a lo bruto sobre la interfaz que le digamos. Esto que parece tan simple se hace con algo como:
#Creamos la entrada de la tabla de enrutamiento
echo 20$SUCURSAL ruta$SUCURSAL >> /etc/iproute2/rt_tables #Le decimos al kernel que los paquetes con la marca X sigan la ruta que hemos creado antes
ip rule add fwmark $SUCURSAL table ruta$SUCURSAL

#Por ultimo configuramos la ruta para que enchufe los paquetes marcados por la interfaz que queremos
ip route replace dev vmnet$SUCURSAL table ruta$SUCURSAL

Con lo anterior conseguimos poder entrar desde el exterior a cada una de las máquinas de la red interna siguiendo las redirecciones de puertos que hayamos hecho.

Para poder salir hay que dar otra vuelta de tuerca más, que ha sido la que me ha traído los quebraderos de cabeza y que por haberlo conseguido estoy escribiendo esto ahora mismo. Para salir, no solo tenemos que hacer algo igual a lo anterior sino que además debemos guardar hacer NAT de tal forma de que cuando nos respondan del exterior, esos paquetes no se pierdan por la pila TCP/IP del kernel. Al final «sólo» hay que hacer:
#Marcamos los paquetes que nos entran, como antes, pero con otra marca, of course
iptables -A PREROUTING -t mangle -d ! $IP -i vmnet$SUCURSAL -j MARK --set-mark 100$SUCURSAL

#Creamos la tabla de ruta de salida
echo 40$SUCURSAL salida$SUCURSAL >> /etc/iproute2/rt_tables

#Decimos que los paquetes con marca X usen la tabla Y
ip rule add fwmark 100$SUCURSAL table salida$SUCURSAL

#Le decimos al kernel que los paquetes que usen esa ruta sean enviados por la ip externa de la sucursal
ip route replace via $IP table salida$SUCURSAL

Esto es algo parecido a lo que teniamos antes. Ahora solo nos falta hacer el NAT. En un principio intenté hacer un NAT normal y corriente con MASQUERADE pero el problema es que las respuestas no se enrutaban bien. Así que al final descubrí lo que tanto me ha costado y es:
#Le ponemos otra marca a los paquetes que entren, por ejemplo la misma de antes
iptables -A PREROUTING -t nat -d ! $IP -i vmnet$SUCURSAL -j CONNMARK --set-mark 100$SUCURSAL

Aunque sea la misma marca uso dos «targets» diferentes. El primero era para que iproute2 pudiera verla y esta de ahora es para que el propio iptables pueda verla en la siguiente orden:
#Le decimos al kernel que con los paquetes con esa nueva ultima marca, haga un Source NAT enviandolos por la unica interfaz fisica real de la maquina pero que ponga como ip de origen, la ip externa de esa sucursal
iptables -t nat -A POSTROUTING -p all -m connmark --mark 100$SUCURSAL -o eth1 -j SNAT --to-source $IP

Y si juntamos todo esto, le añadimos los bucles correspondientes y los ficheros de configuración que se nos ocurran...TATATACHÁN!!!! Ya tenemos una máquina que contiene N routers sobre N redes privadas idénticas

Esto funciona. Casi seguro que se podría optimizar y que se podría haber hecho de otras formas más sencillas. Así que si a alguno se le ocurre alguna que no dude en escrbir un comentario y exponerla.

No hay comentarios: