lunes, 7 de marzo de 2011

Enrutamiento avanzado automático en Linux.

Una situación que se va volviendo más común es tener más de una conexión a internet en los servidores. El problema que se presenta, entonces, es que cuando nos llega un paquete proveniente de internet por una conexión, éste debe ser contestado por la misma por donde llegó y no simplemente por la ruta por defecto.

El comportamiento normal sería el siguiente:

Red local, eth0
ip 192.168.1.1
mascara 255.255.255.0

Primera salida a internet, eth1
ip 1.2.3.4
mascara 255.255.255.0
ruta por defecto 1.2.3.254

Primera salida a internet, eth2
ip2.3.4.5
mascara 255.255.255.0
ruta por defecto 2.3.4.254

Segun este esquema, las tablas de ruteo quedarían así:


Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0          0 eth0

1.2.3.0         0.0.0.0         255.255.255.0   U         0 0          0 eth1
2.3.4.0         0.0.0.0         255.255.255.0   U         0 0          0 eth2

0.0.0.0         1.2.3.254       0.0.0.0         UG        0 0          0 eth1
0.0.0.0         2.3.4.254       0.0.0.0         UG        0 0          0 eth2 



Como se puede ver, aparecen dos rutas por defecto. Sin embargo el sistema utilizará siempre la primera que haya a no ser que la interfaz esté caída, o sea que falte la letra "U" (de up).

En qué nos afecta esto? Supongamos que nos viene un paquete destinado a la ip 1.2.3.4 proveniente de algún lugar de internet, digamos 5.5.5.5. Cuando el sistema mande la respuesta, esta saldrá por la placa eth1, porque es la primera ruta que sirve para alcanzar la ip 5.5.5.5 y todo funciona bien.

Pero qué pasa si viene un paquete proveniente, digamos, de 6.6.6.6, destinado a 2.3.4.5. El paquete llegará sin problemas pero cuando haya que enviar la respuesta, esta saldrá, de nuevo, por la eth1. Pero resulta que la IP de origen del paquete de respueta tiene que ser, obviamente, 2.3.4.5 y el proveedor de internet al que llegamos por la eth1 no querrá rutear paquetes con esa ip de origen. El paquete debería haber salido por la eth2, dirigido a su router correspondiente, el 2.3.4.254.

Lo que necesitamos, es alguna manera de decirle al sistema qué hacer en esta situación, y ahí es donde aparece la capacidad de ruteo avanzado que tiene Linux.

El documento  Linux Advanced Routing and Traffic Control how-to
 explica claramente este problema y la manera de solucionarlo. Lo que yo les presento ahora es un script que se encarga de hacer todo automáticamente.


La situación es  que tenemos dos rutas por defecto y debemos utilizar cada una según corresponda.

ATENCION: esto no se trata de balancear cargas por las dos conexiones. Se trata simplemente de que ambas sean utilizables desde internet.




Para solucionar esta situación, lo que hace es crear una tabla para cada placa de red que haya. En cada una tiene rutas para llegar a las demás redes, y la ruta por defecto que corresponda a esa placa. De esa forma, se puede configurar en cada placa de red una ruta por defecto diferente.

Para seleccionar cuál de todas estas tablas de ruteo es la que debemos utilizar en cada momento, se debe crear reglas que seleccionan la tabla correspondiente en función de la IP de origen del paquete. La IP de origen es lo que distingue una placa de red de otra, así que nos sirve para saber por qué placa está saliendo el paquete.


Para su funcinamiento, el script utiliza la información de los archivos de configuración de la red, que se encuentran en /etc/sysconfig/network-scripts. Al menos en los sistemas que siguen la Linux Standard Base, este directorio contiene archivos con el nombre ifcfg-<nombre de la placa de red>. Por ejemplo /etc/sysconfig/network-scripts/ifcfg-eth1 contendría:

DEVICE=eth
BOOTPROTO=static
IPADDR=1.2.3.4
NETMASK=255.255.255.0
GATEWAY=1.2.3.254
ONBOOT=yes
METRIC=10
MII_NOT_SUPPORTED=no
USERCTL=no
DNS1=127.0.0.1
RESOLV_MODS=no
IPV6INIT=no
IPV6TO4INIT=no
ACCOUNTING=no


Lo que nos interesa de este archivo son la IPADDR, NETMASK y GATEWAY. Con estos datos podemos generar automáticamente lo que necesitamos.

A continuación el script:

#limpiar reglas anteriores por si lo ejecutamos de nuevo

ip rule show | awk 'BEGIN{FS=":"}{if(index($2,"lookup main")==0 && index($2,"lookup default")==0 && index($2,"lookup local")==0) system ("ip rule del " $2) }'


#recorrer las intefaces de red
#para cada una crear una tabla

cuenta=0
for ifcfg in /etc/sysconfig/network-scripts/ifcfg-*
do
      cuenta=$(( cuenta + 1 ))
      TABLE=100$cuenta

      . $ifcfg
      echo "DEVICE=$DEVICE"

      export `ipcalc -n $IPADDR $NETMASK`
      NET=$NETWORK/$NETMASK

      ip route flush table $TABLE

      ip route add $NET dev $DEVICE src $IPADDR table $TABLE
      if [ "$GATEWAY" != "" ]
      then
         ip route add default via $GATEWAY table $TABLE
      fi

      ip route add $NETWORK dev $DEVICE src $IPADDR


      ip rule add from $IPADDR table $TABLE
      ip rule add fwmark $TABLE table $TABLE



      tables="$tables $TABLE"
      if [ "$GATEWAY" != "" ]
      then
         ip route add default via $GATEWAY table $TABLE
      fi

      echo 
      echo 
      echo 
      echo 
done



for TABLE in $tables 
do
   for ifcfg in /etc/sysconfig/network-scripts/ifcfg-*
   do
      . $ifcfg
      export `ipcalc -n $IPADDR $NETMASK`
      NET=$NETWORK/$NETMASK

      ip route add $NET dev $DEVICE table $TABLE
   done

done


echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter
ip route flush cache