Configuración de Redes en Debian
Configuración de Redes en Debian
Índice
1. Propósito
2. Interfaces
2.1. Nombre de las interfaces
2.2. Manipulación de los nombres
2.3. Interfaces dummy
2.4. Lo que se avecina
3. Configuración básica
3.1. Método universal
3.1.1. Configuración estática
3.1.2. Configuración dinámica
3.2. Método de debian
3.2.1. Configuración estática
3.2.2. Configuración dinámica
3.2.3. Consulta de interfaces
3.2.4. Configuración avanzada
4. Linux como bridge
4.1. Método universal
4.2. Método de debian
5. VLANs en linux
5.1. Configuración
5.1.1. Método universal
5.1.2. Método de debian
5.2. Multiplexación de VLANs
5.2.1. Bridge por cada VLAN
5.2.2. Bridge vlan
6. Linux como router
6.1. Generalidades
6.2. Encaminamiento simple
6.3. Encaminamiento múltiple
6.4. Balanceo de carga
7. Proxy ARP con linux
7.1. Concepto
7.2. Configuración
8. Control de tráfico (QoS)
8.1. Concepto
8.2. Planificación de tráfico (qdisc)
8.2.1. Planificaciones sin clases
8.2.2. Planificaciones con clases
8.2.3. Planificación del tráfico entrante
8.3. Cálculos orientativos
8.3.1. Pérdida por encapsulación
8.3.2. Flujo de tráfico de confirmación
8.4. Filtros
8.4.1. Filtro route
8.4.2. Filtro fw
8.4.3. Filtro u32
8.4.4. Filtro basic (ematch)
8.4.5. Acciones y control sobre el filtro (action/police)
8.4.6. Comprobación
8.5. Caso de implementación
8.5.1. Control del tráfico saliente
8.5.2. Control del tráfico entrante
8.5.3. Script de configuración
9. Apéndice: Vademécum sobre iproute2
1 Propósito
El documento pretende mostrar cómo pueden configurarse las interfaces de red de un servidor montado sobre una distribución debian, desde la necesidad más
simple que es dotar a una interfaz de una configuración válida a casos muchísimo más complicados en que se tienen varias interfaces y se quiere que el tráfico
transite por ellas. No hay, porque no es habitual, mención a la configuración de interfaces inalámbricas.
La mejor forma de probar todo lo expuesto aquí es hacer uso de máquinas virtuales que permitan emular el comportamiento completo de un ordenador y la
existencia de redes separadas. A este efecto tanto qemu como VirtualBox son adecuados. También es posible hacerlo sobre LinuX Containers. Si lo que se
quiere tan sólo es comprobar la validez de la sintaxis de los comandos es posible usar interfaces dummy.
Es importante reseñar que para acabar de manipular y encauzar completamente el tráfico, quizás se necesite redirigir y enmascarar tráfico modificando las
orígenes y destinos de los paquetes. Esto último se hace fundamentalmente con iptables, aunque en los casos en que hay definidas interfaces bridge habrá
también que echar mano de ebtables.
Al hilo de la configuración de interfaces el autor ha desarrollado un script global de configuración en python que a partir de una declaración de interfaces en
formato xml genera el fichero /etc/network/interfaces pertinente. En realidad el script tiene un propósito mucho más amplio y de esta tarea se encarga
docs.iescdl.es/~josem/redes.html 1/49
27-10-2019 Configuración de redes en debian
uno de sus módulos.
La última parte del documento está dedicada al control de tráfico, también conocido como calidad de servicio.
¡ADVERTENCIA! Hayalgunas partes del texto dedicado a la configuración de interfaces marcadas como desaconsejadas. Esto es así, porque presentan scripts
propios quizás no lo suficientemente comprobados y que pueden no funcionar en todos los casos o crear incompatibilidades con otros scripts oficiales para
ifupdown que provean los paquetes de debian. Si se usan, han de usarse con la previsión de que, si ocurre algún error, debe intentarse también la
configuración manual para averiguar si el error está en la propia configuración ideada o en la escritura deficiente de los scripts.
2 Interfaces
2.1 Nombre de las interfaces
Por lo general, en linux las interfaces ethernet cableadas reciben el nombre de ethX, siendo X un número natural incluido el 0: 0, 1, 2, 3, etc.
Además, suele ser indispensable tener definida la interfaz virtual de loopback (o de bucle interno), que recibe el nombre de lo.
El número que reciba cada interfaz física de forma natural depende del orden en que sean sean detectadas durante el arranque, de manera que la primera
recibirá el nombre eth0, la segunda eth1, etc. Sin embargo, como se verá más adelante, el sistema conserva la memoria de las interfaces que tuvo
conectadas, de modo que esta regla general comúnmente acaba por no cumplirse. Por ejemplo, supongamos una máquina con una tarjeta de red, cuyo
sistema ya ha sido ejecutado al menos una vez. En este caso, la tarjeta se llamará eth0, puesto que no hay ni ha habido otra. Si tiempo después
conectamos otra interfaz de red, podría darse el caso de que esta nueva interfaz sea detectada antes durante el proceso de arranque. Sin embargo, la
interfaz primitiva ya tiene por nombre eth0 y lo conservará, así que esta nueva se nombrará eth1, a pesar de ser detectada antes. De hecho, la
conclusión no habría cambiado si hubiéramos hecho desaparecer la interfaz primitiva: aunque ya no existiera seguiría ocupando el índice 0 y la única
interfaz disponible (la nueva interfaz) sería eth1. Por supuesto, todo esto es manipulable y ya se verá cómo: lo primero es conocer cuáles son las
interfaces disponibles en nuestro sistema.
En lo referente a estas interfaces disponibles, hay dos consideraciones distintas: las interfaces de red que fisicamente existen en nuestro sistema; y las
interfaces que verdaderamente están disponibles. No son exactamente las mismas, puesto que puede haber interfaces disponibles que sean virtuales y no
físicas (lo es el mejor ejemplo); o interfaces físicas que no estén disponibles, porque no haya driver para ellas.
Para consultar las interfaces físicas detectadas por el sistema (todo el hardware moderno es PnP) basta con usar lspci, si la tarjeta es pci, o lsusb,
si es usb (esto último será muy poco habitual en servidores):
En el ordenador en que ahora mismo escribo hay dos interfaces físicas: una interfaz wireless con chip de Atheros AR5001X+, y otra cableada con chip
de Realtek RTL-8110SC/8169SC.
Por otro lado, la consulta de las interfaces disponibles, puede hacerse listado el contenido de /sys/class/net:
$ ls /sys/class/net
eth0 lo wlan0
En ambos casos se obtienen obviamente las mismas interfaces: la de loopback (lo), la interfaz de cable (eth0) y la interfaz wireless (wlan0), cuya
descripción se sale del propósito de este documento.
En ocasiones, el nombrado que hace linux de las interfaces no es el que nos parece más conveniente: bien porque tenemos interés en asignarle a una
determinada interfaz un nombre concreto, bien porque han desaparecido interfaces cuyo nombre queremos recuperar.
El culpable de asignar estos nombres es udev que apunta en el fichero /etc/udev/rules.d/70-persistent-net.rules los nombres que se
asignan a cada interfaz
$ cat /etc/udev/rules.d/70-persistent-net.rules
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.
En el fichero se asocian direcciones MAC con nombres de interfaz. Basta con alterar estas asociaciones para cambiar el nombre real de la interfaz.
Nótese que se ha añadido el calificativo real, porque este nombre a su vez podrá ser cambiado con el comando ip.
docs.iescdl.es/~josem/redes.html 2/49
27-10-2019 Configuración de redes en debian
2.3 Interfaces dummy
Linux dispone de un módulo llamado dummy que genera interfaces ficticias: no tienen utilidad como interfaces de accesos a la red, pero sí dentro de la
propia máquina. Podemos, por ejemplo, poner a escuchar servicios en estas interfaces y hacer oruebas locales de conexión. O, para el caso que nos
ocupa, usarlas para configurarlas y desconfigurarlas, sin más utilidad que comprobar que efectivamente funcionan nuestros comandos.
El parámetro numdummies le indica al módulo cuántas interfaces ficticias de red queremos crear. Si no lo especificamos, se creará sólo una. Hecho lo
anterior, podemos comprobar que ahora disponemos de dos interfaces más:
# ls /sys/class/net/
br0 dummy0 dummy1 eth0 lo
A partir de ahora podemos tratar estas interfaces como otra interfaz de red ethernet más, aunque sabiendo que no conectan en realidad con ninguna red.
Si se quiere probar alguno de los comandos que se presentan a continuación y no se dispone de software de virtualización, se puede echar mano de
estas interfaces.
Hemos comenzado afirmando que las interfaces de cable ethernet en linux se denominan con el prefijo eth seguido de un número consecutivo.
Siempre ha sido así, pero muy recientemente, por mor de systemd, tan recientemente que no ha pillado a jessie, pero sí se verá en stretch, la
nomeclatura ha cambiado.
Así pues, si su propósito es tratar con jessie (como lo es en general el de estos apuntes) o una versión aún más antigua, no tiene porque cuidarse de lo
expuesto bajo el presente epígrafe: sus interfaces se llamarán ethX y no hay mucho más que saber. En caso contrario, o que le pìque la curiosidad y no
desee encontrarse con sorpresas en el futuro, continué leyendo.
El problema de la nomenclatura antigua es que el sistema va asignado los nombres a las interfaces según van siendo cargadas en el sistema con el driver
apropiado, pero este orden de carga es impredecible; así que no podemos asegurar que una interfaz que ha sido llamada eth0, siga siendo eth0 en el
futuro. Por ejemplo, podría darse el caso de que en un sistema de una interfaz pincháramos una tarjeta adicional y que al arrancarlo esta nueva tarjeta
fuera la primera en cargarse. Como consecuencia, la nueva tarjeta sería eth0 y la antigua pasaría a ser eth1. Como en nuestro sistema teníamos
configurada la interfaz eth0, dicha configuración que se pensó para una interfaz se aplicaría a la otra. Esto puede derivar, incluso, en problemas de
seguridad.
La defensa de jessie frente a este problema está en el fichero /etc/udev/rules.d/70-persistent-net.rules, que ya se vio que guerdaba el
nombre de las interfaces asociandolo a la mac.
systemd ensaya otro método distinto: asignar nombres de manera que estos estén más ligado al rol de la propia interfaz (léanse los principios que
argumentan los responsables de freedesktop.org). La consecuencia es que el prefijo de las interfaces pasará a ser eno para interfaces integradas, ens
para interfaces PCI, enx para interfaces en que se use un nombre formado a partir de su propia MAC, o eth si se decide renunciar e estas novedades y
hacer que linux siga asignando los nombres del mismo modo que hasta ahora.
Si se desea esto último, basta añadir a las opciones del kernel net.ifnames=0, lo cual puede hacerse cómodamente en debian, editando el fichero
/etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="quiet net.ifnames=0"
En caso contrario, las interfaces recibirán nombres que comienzan por eno o ens. Por ejemplo, en una máquina virtual qemu con esta tarjeta:
La tarjeta recibe el nombre ens16 (ya que 0x10 es 16 en decimal). No obstante, puede seguir escribiéndose el fichero 70-persistent-
net.rules para asignar los nombres que deseemos (incluidos los tradicionales).
Si deseamos usar los nombres basados en la MAC, podremos también copiando y alterando el fichero /lib/udev/rules.d/80-net-setup-
link.rules:
# cp /lib/udev/rules.d/80-net-setup-link.rules /etc/udev/rules.d/
NAME=="", NAME="$env{ID_NET_NAME_MAC}"
Podemos conocer los posibles nombres que puede adoptar una interfaz con la orden:
3 Configuración básica
docs.iescdl.es/~josem/redes.html 3/49
27-10-2019 Configuración de redes en debian
Para la configuración de las interfaces podemos usar una herramienta universal, válida para cualquier sistema linux o utilizar el método de definción que
proporcina debian y sirve en cualquier distribución que derive de ella. Es recomendable usar este segundo método, pero conviene conocer el primero, por si
nos encontramos en algún momento frente a una distribución distinta.
1. La asignación de ip a la interfaz.
2. La asignación de una puerta de enlace.
3. La definición de los servidores DNS para la resolución de nombres.
Para asignar ip a una interfaz basta con usar el comando ip dos veces: una para la asignación en sí y otra para levantar la interfaz:
Para ver lo que hemos hecho, podemos pedir la configuración de la dirección de eth0 (sin expresar qué interfaz se quiere ver se muestran
todas):
Obsérvese que se muestra la dirección ip, pero también el estado de la interfaz: UP significa que está habilitada y LOWER_UP que el cable se
haya conectado al otro extremo a un dispositivo de red. Así pues, si la interfaz tiene una configuración correcta, deberíamos poder conectar a
los dispositivos de la misma red. Si queremos salir de la red, sin embargo, tendremos que definir también la puerta de enlace predeterminada
(supongamos que es la 192.168.1.1):
Habría sido posible usar la palabra default en vez de declarar explícitamente 0.0.0.0/0:
Por último, es necesario definir los servidores DNS que se usarán para resolver nombres dentro del fichero /etc/resolv.conf. Este
fichero deberá tener este aspecto:
$ cat /etc/resolv.conf
domain milan.com
search milan.com
nameserver 8.8.8.8
nameserver 8.8.4.4
Los servidores se declaran con líneas que comienza con la palabra nameserver. La línea domain debería contener el nombre del dominio
al que pertenece la propia máquina, y la línea search los dominios de búsqueda con los que se completará un nombre cuando de este no se
indique ningún dominio, sino sólo el nombre de la máquina.
Para desactivar y desconfigurar una interfaz podemos recurrir otra vez a ip:
Al desactivar una interfaz, todas las entradas en la tabla de encaminamiento que tengan una puerta de enlace en la misma red que la interfaz
desactivada, desaparecerán.
OBSOLETO
Aunque es recomendable usar el comando ip, es habitual que aún esté disponible en linux el comando ifconfig o quizás, si es muy
antiguo, sólo esté disponible este último. En este caso, el primero y segundo paso se realizan del siguiente modo:
docs.iescdl.es/~josem/redes.html 4/49
27-10-2019 Configuración de redes en debian
Para obtener la configuración automática de un servidor DHCP es necesario usar un cliente. El más habitual es dhclient, aunque en algunos
linux mínimos, como los empotrados, se usa udhcpc.
# dhclient -v eth0
Esto configurará todo lo necesario, incluidos el encaminamiento y los servidores DNS. Si se quiere cancelar la petición:
# dhclient -r
Para configurar estáticamente una interfaz se pueden incluir las siguientes líneas:
auto eth0
iface eth0 inet static
address 192.168.1.25
gateway 192.168.1.1
Como no se ha indicado ningún valor para la máscara (para lo cual se puede usar la palabra netmask), se presupone la máscara
predeterminada 255.255.255.0. Además, la línea auto eth0 indica que la interfaz debe será activada automáticamente durante el
arranque. Una variante a esto es allow-hotplug eth0. La diferencia es que en este caso se activará (y desactivará) según los eventos que
reciba el sistema sobre el enlace, dicho de otro modo: si se detecta el cable conectado, se procederá a activar la interfaz; si se detecta en algún
momento la desconexión del cable, se procederá a la desactivación.
Si quisiéramos que la interfaz perteneciera a la red 192.168.0.0/23 entonces habría que indicar la máscara forzosamente:
auto eth0
iface eth0 inet static
address 192.168.1.25
netmask 255.255.254.0
gateway 192.168.0.1
auto eth0
iface eth0 inet static
address 192.168.1.25/23
gateway 192.168.0.1
También pueden indicarse las ip de red (network) y de broadcast (broadcast), pero no es necesario puesto que debian es capaz de
calcularlas.
Por último, si se quiere activar o desactivar una interfaz, se pueden usar las órdenes ifup e ifdown:
La configuración de los servidores dns puede hacerse escribiendo manualmente en /etc/resolv.conf o bien escribirse directamente en
/etc/network/interfaces, si se tiene instalado el paquete resolvconf:
auto eth0
iface eth0 inet static
address 192.168.1.25
dns-nameservers 8.8.8.8 8.8.4.4
La configuración por dhcp es semejante, aunque en este caso no hay que especificar ningún dato:
auto eth0
iface eth0 inet dhcp
Cuando se configuran interfaces a través de ifupdown, es útil conocer qué interfaces tenemos declaradas en el fichero de configuración y
cuáles hemos habilitado y deshabilitado con ifup e ifdown. Para ello vale el comando ifquery.
Supongamos que tenemos declaradas tres interfaces: lo, eth0 y eth1, pero sólo lo y eth0 se habilitaron con ifup (o la directiva auto
que las habilitó durante el arranque). En este caso:
# ifquery --list
lo
docs.iescdl.es/~josem/redes.html 5/49
27-10-2019 Configuración de redes en debian
eth0
eth1
y, sin embargo:
# ifquery --state
lo=lo
eth0=eth0
Lo primero nos da todas las interfaces declaradas en /etc/network/interfaces con auto; lo segundo, sin embargo, nos muestra las
interfaces que se habilitaron mediante ifup. Incluso si eth1 estuviera habilitada de forma manual, no aparecería en el segundo listado de
interfaces habilitadas.
Si se desea listas las interfaces declaradas con allow-hotplug se puede hacer lo siguiente:
Si echamos un vistazo a versiones recientes de debian, veremos que existe un directorio llamado /etc/network/interfaces.d, que
permite separar las definiciones de las distintas interfaces en distintos ficheros. Esto es así, porque dentro de /etc/network/interfaces
existe incluida de serie una directiva source para que se lean todos los ficheros que se incluyen en dicho directorio:
source interfaces.d/*
Puede ser buena idea usar esta posibilidad si tenemos muchas interfaces definidas en nuestro servidor.
debian permite ejecutar órdenes antes de levantar una interfaz (pre-up), tras haberla levantado (up), antes de bajar la interfaz (down) o
juntamente después de haberla bajado (post-down). Hay dos modos de indicarlo:
Directamente en el fichero en que se declaran las interfaces. Por ejemplo, si quisiéramos enmascarar todo lo que saliera por la interfaz
eth0, podríamos hacer lo siguiente:
auto eth0
iface eth0 inet static
address 172.22.0.2
pre-up iptables -t nat -A POSTROUTING -o $IFACE -j SNAT --to-source $IF_ADDRESS
post-down iptables -t nat -D POSTROUTING -o $IFACE -j SNAT --to-source $IF_ADDRESS
Creando ejecutables dentro de los directorios if-pre-up.d, if-up.d, if-down.d y if-post-down.d que hay en
/etc/network.
Si se observan las líneas de ejemplo escritas, se verá que se han usado las variables IFACE e IF_ADDRESS, que almacenan el nombre y la ip
de la interfaz, respectivamente. Esto es posible porque el sistema de gestión de interfaces de debian facilita una serie de variables para que
puedan ser usadas tanto en las líneas empotradas en el fichero interfaces, como en los ejecutables independientes. Pueden consultarse
aquí todas, aunque las más interesantes son:
Nombre Significado
$IFACE Nombre de la interfaz.
$METHOD Método de consecución de la ip (static, dhcp, etc.).
$MODE start/stop según activación o desactivación.
$PHASE pre-up, up, down o post-down.
$IF_<OPT> Valor de la opción <OPT>.
Hay una diferencia bastante importante entre incluir directamente los comandos o crear un ejecutable aparte: cuando se incluyen líneas, estas
pertenecen a la configuración de una interfaz y, por tanto, sólo se ejecutan cuando se levanta o baja la interfaz. En cambio, cuando se crean
ejecutables, estos se ejecutan sea cual sea la interfaz que se levanta o baja, y hay que obrar en consecuencia. Por ejemplo, lo equivalente a las
dos líneas anteriores, sería crear dos ficheros:
$ cat /etc/network/if-pre-up.d/masquerade
[ "$IFACE" = "eth0" ] || exit 0
iptables -t nat -A POSTROUTING -o $IFACE -j SNAT --to-source $IF_ADDRESS
$ cat /etc/network/if-post-down.d/masquerade
[ "$IFACE" = "eth0" ] || exit 0
iptables -t nat -D POSTROUTING -o $IFACE -j SNAT --to-source $IF_ADDRESS
En los que si la interfaz no es eth0 se acaba la ejecución. Dado que ambos ficheros son exactamente iguales salvo por el hecho de que hay
que añadir (-A) o borrar (-D) la regla, podríamos crear un único fichero que analizara si estamos levantando o bajando una interfaz:
$ cat /etc/network/if-pre-up.d/masquerade
[ "$IFACE" = "eth0" ] || exit 0
[ "$MODE" = "start" ] && A="A" || A="D"
iptables -t nat -$A POSTROUTING -o $IFACE -j SNAT --to-source $IF_ADDRESS
docs.iescdl.es/~josem/redes.html 6/49
27-10-2019 Configuración de redes en debian
# cd /etc/network/if-post-up.d
# ln -s ../masquerade
El esquematizado es el caso más sencillo en que el servidor se comporta como un switch de dos puertos (eth0 y eth1), esto es, como bridge o puente.
Para resolver esto, lo que se hace es crear una interfaz virtual bridge (a las que se les dar por nombre br0, br1, etc.) a la que se añaden todas las interfaces que
queramos que participen en la red. En este caso, no se produce encaminamiento de capa 3, como se produciría si el servidor separara dos redes distintas, sino
conmutación de capa 2.
Además, si queremos que el servidor sea accesible desde la red, a la interfaz bridge se le puede dar una dirección ip de dicha red.
Partiendo de que no hay ninguna interfaz configurado ni habilitada, lo primero es crear la interfaz bridge:
Creada la interfaz, opcionalmente, se le puede asignar una dirección MAC concreta a voluntad. Por ejemplo, si queremos asignarle la MAC de eth0:
Por último, debemos habilitar las tres interfaces y asegurarnos de que las dos interfaces físicas están en modo promiscuo:
Con esto ya tendríamos creado y activado el puente y debería haber comunicación entre las máquinas ambos segmentos. Podemos echas un vistazo a
cómo ha quedado la configuración con el comando:
También podemos asignarle una ip a la interfaz bridge para poder acceder al servidor a través de la red (supongamos que es la 172.22.0.0/16):
Si queremos deshacer el puente, basta desligar las interfaces físicas del puente y eliminarlo finalmente:
OBSOLETO
El proceso es equivalente al anterior: crear la interfaz bridge, añadir las interfaces requeridas y levantarlas. brctl nos permite realizar las
operaciones de creación y adición (y las contrarias de desligado y destrucción):
docs.iescdl.es/~josem/redes.html 7/49
27-10-2019 Configuración de redes en debian
Para levantar las interfaces, en cambio, usaremos ip (o ifconfig). brctl también permite ver los componentes del bridge:
# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.deadbeefc22e no eth0
eth1
Algo que no puede hacerse con bridge o ip de iproute2 es habilitar el protocolo SPT:
Para el sencillo bridge anterior, basta esta configuración mínima, que se encarga de levantar las interfaces y poner las en modo promiscuo sin necesidad
de declararlo:
auto eth0
iface eth0 inet manual
auto eth1
iface eth1 inet manual
auto br0
iface br0 inet manual
bridge_ports eth0 eth1
bridge_maxwait 2
Podemos añadir una directiva address a la declaración de br0, si queremos que el bridge disponga de ip en la red.
DESACONSEJADO
Aunque el anterior es el método recomendable y estándar, si deseamos prescindir de bridge-utils, podemos intentar construir dos scripts que
usen iproute2. El primero:
#!/bin/bash
ACTION="exit 0"
function bridge_ports() {
# cd /etc/network
# cp /donde/se/encuentre/if-pre-up-bridge-iproute2.txt if-pre-up.d/bridge-iproute2
# chmod +x if-pre-up.d/bridge-iproute2
# ln -s ../if-pre-up.d/bridge-iproute2 if-post-down.d/bridge-iproute2
El segundo script:
docs.iescdl.es/~josem/redes.html 8/49
27-10-2019 Configuración de redes en debian
#!/bin/bash
ACTION="exit 0"
function is_bridge() {
[ -d /sys/class/net/$1/bridge ]
}
function is_bridged() {
[ d /sys/class/net/$1/master ]
# cd /etc/network
# cp /donde/se/encuentre/if-up-bridge-iproute2.txt if-up.d/bridge-iproute2
# chmod +x if-up.d/bridge-iproute2
# ln -s ../if-up.d/bridge-iproute2 if-down.d/bridge-iproute2
Estos scripts esperan que nos refiramos a las interfaces involucradas en el fichero interfaces de esta manera:
auto br0
iface br0 inet manual
allow-hotplug eth0
iface eth0 inet manual
master br0
allow-hotplug eth1
iface eth1 inet manual
master br0
5 VLANs en linux
En linux, es sumamente fácil crear, para una misma interfaz física, subinterfaces en distinta VLAN. Esto permite dividir el tráfico en varias redes lógicas que
viajan por el mismo cable. Si quiere luego desdoblarse este tráfico etiquetado (según la norma IEEE 802.1q) en distintos cables, puede disponerse al otro
extremo del cable un switch gestionable que soporte esta misma norma, o bien, un router neutro de los que soportan dd-wrt, tomato u openwrt. Esto último
se sale del propósito del documento, pues sólo se pretende aquí dejar indicado cómo crear las subinterfaces en el servidor.
De nuevo podemos usar un método general o el método propio de debian basado en escribir en /etc/network/interfaces.
5.1 Configuración
Tradicionalmente para esta tarea se ha venido usando el comando vconfig; pero como lo conveniente es ir migrando iproute2 se indicará
aquí con preferencia cómo hacerlo con este último comando.
Para crear una subinterfaz para eth0 en la vlan 100 basta con lo siguiente:
docs.iescdl.es/~josem/redes.html 9/49
27-10-2019 Configuración de redes en debian
Obsérvese el nombre: se ha usado el que se ha usado por seguir una de las variantes con que permite crear nombres vconfig (eth0.100,
eth0.0100, vlan100 o vlan0100). Sin embargo, con ip no debemos ceñirnos a ninguna forma concreta y podemos poner el nombre a la
interfaz que deseemos.
Una vez creada la interfaz podemos configurarla (y desconfigurarla) exactamente igual que si se tratara de una interfaz real:
Una vez creada podemos comprobar su configuración y a qué VLAN pertenece añadiendo la opción -d al comando ip:
OBSOLETO
Hecho esto podemos usar vconfig tanto para crear una interfaz:
vconfig ha usado este tipo de nombre porque es el que tiene habilitado de modo predeterminado. Si se quisiera utilizar una variante
distinta habría que especificarlo antes:
Para poner en práctica este método debemos escribir algo así en el fichero /etc/network/interfaces:
auto eth0.1
iface eth0.1 inet static
address 192.168.100.1
Esto permitiría crear la interfaz eth0.1 que, obviamente, es subinterfaz de eth0 en la VLAN 1. debian, de modo sencillo, sólo permite
este tipo de nombres. Si quisiéramos otro, entonces tendríamos que recurrir a órdenes de preconfiguración y postconfiguración:
auto vlan1
iface vlan1 inet static
address 192.168.100.1
pre-up ip link add link eth0 name $IFACE type vlan id 1
post-down ip link del $IFACE
DESACONSEJADO
Los scripts propuestos para crear puentes sin recurrir al paquete bridge permiten también crear una interfaz vlan con un nombre arbitrario,
aunque ambos sean distinto tipo de interfaz. Sin embargo, dado que estos scripts permiten resolver el problema de crear un bridge vlan
completo, se ha considerado oportuno incluir esta solución también en ellos:
Como se ve, basta con incluir el parámetro vlan con el nombre que debería haber tenido la interfaz. En este caso eth0.1, porque se
pretende crear un subinterfaz de eth0 para sostener la vlan 1.
OBSOLETO
Hasta no hace demasiado tiempo, ifupdown usaba para crear interfaces vlan el comando vconfig, de ahí que en muchos tutoriales de
internet citen la opción vlan_raw_device (o vlan-raw-device que es equivalente) para declarar la interfaz física para la que se
definía la subinterfaz de la vlan. Ya no tiene efecto, no al menos, si no se instala el paquete vlan en el que se incluye vconfig.
docs.iescdl.es/~josem/redes.html 10/49
27-10-2019 Configuración de redes en debian
5.2 Multiplexación de VLANs
Vista la configuración, supongamos que queremos disponer, por un lado, de un enlace troncal por el que viajan etiquetadas distintas VLANs y, por otro,
de un conjunto de interfaces físicas, cada una de las cuales pretendemos asociar a una VLAN distinta. Por estas interfaces el tráfico ya no sale
etiquetado. Esquemáticamente podríamos dibujarlo así:
Este problema puede resolverse de dos modos distintos: uno más tradicional en que usamos un puente para cada VLAN; y otro que consiste en construir
un verdadero swith con soporte para VLANs.
La primera solución consiste en definir tantas subinterfaces a eth0 como VLANs viajen por el enlace troncal, para después ir creando
interfaces bridge, de modo que a cada una añadamos una subinterfaz y la interfaz física que se corresponde con esa subinterfaz (vlan). Por
ejemplo, para el caso mostrado habría que incluir dentro de un mismo puente la subinterfaz de eth0 correspondiente a la VLAN 10
(eth0.10) y la interfaz física eth1; y así mismo con las otras dos VLANs.
Y activándolas para que funcionen (quizás haya además que ponerlas explícitamente en modo promiscuo):
Luego basta con crear interfaces puente para cada VLAN y asignar las interfaces correspondientes:
Que no despiste el nombre de las interfaces vlanXX: aunque las hemos nombrado así, son interfaces bridge. Por último, habrá que activarlas:
Basta con esto: linux se encarga de etiquetar y desetiquetar el tráfico según se salga por una u otra interfaz.
Adicionalmente, si queremos que el servidor tenga una ip en cada VLAN, podemos asignarles a vlan10, vlan20 y vlan30 una dirección
adecuada.
Esta resolución es bastante sencilla traducirla un fichero /etc/network/interfaces para que ifupdown se encargue por nosotros de
ejecutar las órdenes. En el caso de usar brctl esta sería la configuración:
docs.iescdl.es/~josem/redes.html 11/49
27-10-2019 Configuración de redes en debian
auto eth.10
iface eth0.10 inet manual
auto eth.20
iface eth0.20 inet manual
auto eth.30
iface eth0.30 inet manual
allow-hotplug eth1
iface eth1 inet manual
allow-hotplug eth2
iface eth2 inet manual
allow-hotplug eth3
iface eth3 inet manual
auto vlan10
iface vlan10 inet manual
bridge_ports eth1 eth0.10
bridge_maxwait 1
auto vlan20
iface vlan20 inet manual
bridge_ports eth2 eth0.20
bridge_maxwait 1
auto vlan30
iface vlan30 inet manual
bridge_ports eth3 eth0.30
bridge_maxwait 1
DESACONSEJADO
auto eth.10
iface eth0.10 inet manual
master vlan10
auto eth.20
iface eth0.20 inet manual
master vlan20
auto eth.30
iface eth0.30 inet manual
master vlan30
allow-hotplug eth1
iface eth1 inet manual
master vlan10
allow-hotplug eth2
iface eth2 inet manual
master vlan20
allow-hotplug eth3
iface eth3 inet manual
master vlan30
auto vlan10
iface vlan10 inet manual
bridge yes
auto vlan20
iface vlan20 inet manual
bridge yes
auto vlan30
iface vlan30 inet manual
bridge yes
Esta solución es mucho más completa, porque permite resolver todo con un único puente. De hecho, se pueden crear switches muchísimo más
complejos sin apenas complicación, como que, por ejemplo, una hipotética interfaz eth4 sea también un enlace troncal que permita conectar
en cascada con otro switch.
En este caso lo que se hace es crear un verdadero switch con soporte para VLANs de modo que cada interfaz física es un puerto del switch. El
tráfico que entra por eth0 está etiquetado como de una de las tres VLANs, mientras que en los otros tres puertos se hace algo distinto: entra
tráfico no etiquetado y se etiqueta como de la VLAN que le corresponde; al salir se hace lo contrario, el tráfico de la VLAN que tiene asignada
se desetiqueta.
docs.iescdl.es/~josem/redes.html 12/49
27-10-2019 Configuración de redes en debian
Y a los otros tres puertos su VLAN correspondiente pero indicando que se añada la etiqueta al entrar y se elimine al salir:
# bridge vlan
port vlan ids
eth0 10
20
30
br0 10
20
30
Por supuesto habrá que levantar todas las interfaces (y quizás poner las interfaces físicas en modo promiscuo):
En este caso, si se quiere disponer de direcciones ip pertenecientes a las tres VLANs en el servidor, hay que crear subinterfaces VLAN sobre la
interfaz bridge:
docs.iescdl.es/~josem/redes.html 13/49
27-10-2019 Configuración de redes en debian
DESACONSEJADO
Si queremos automatizar todo este proceso, podemos echar mano de los scripts propuestos para crear un puente y escribir en el fichero de
declaración de las interfaces algo más o menos así:
auto br0
iface br0 inet manual
bridge_vlan 10 20 30
allow-hotplug eth0
iface eth0 inet manual
master br0
vlan 10
vlan 20
vlan 30
allow-hotplug eth1
iface eth1 inet manual
master br0
vlan 10 pvid untagged
allow-hotplug eth2
iface eth2 inet manual
master br0
vlan 20 pvid untagged
allow-hotplug eth3
iface eth3 inet manual
master br0
vlan 30 pvid untagged
Si, además, pretendemos dotar al puente de direcciones ip en las VLANs, será necesario crear interfaces VLAN para él y configurarlas. Por
ejemplo:
auto br0.10
iface br0.10 inet static
address 192.168.10.1
auto br0.20
iface br0.20 inet static
address 192.168.20.1
auto br0.30
iface br0.30 inet static
address 192.168.20.1
Es muy importante que estas interfaces se intenten crear y activar una vez ya exista el puente, de modo que habrá incluirlas en el fichero
después de la declaración de br0.
A diferencia del caso anterior, el encaminamiento es un mecanismos de capa de red (capa 3); y. por tanto. interviene en la decisión de enviar un paquete por una
u otra interfaz la dirección ip de destino
Antes de empezar con ello hay que aclarar un término que se usará a lo largo de todos estos apartados: el de interfaz de salida. En principio, una interfaz de
salida es aquella por la que sale un paquete de nuestra máquina, como contraposición a interfaz de entrada, que es por aquella por la que entra. De hecho, desde
este punto de vista todas las interfaces del router son de entrada y de salida, su papel depende únicamente del sentido del tráfico. Si observamos el dibujo
cuando una máquina de la red 2 hace una petición a una máquina de la red 1, la interfaz de entrada es eth1 y la de salida eth0. Sin embargo, para la respuesta
que realiza el camino inverso la interfaz de entrada es eth0 y la de salida eth1. En muchas ocasiones nos podremos referir a este concepto usando ese término.
En cambio, en ciertos casos podremos referir con el término interfaz de salida a aquella que toman los paquetes para salir a internet, o dicho de otro modos,
aquella por la que saldrán camino de la puerta de enlace predeterminada. Esta forma de entender interfaz de salida es la que ha animado a dividir entres epígrafes
este apartado: el primero presenta el caso más simple en el que hay una única salida a internet; y los dos siguientes aquel en el que existen varias salidas. Cuando
esto último ocurre hay dos estrategias: agregar las salidas, de manera que se comporten a efectos prácticos como una sola, o bien mantenerlas por separado y
crear reglas que encaminen el tráfico a internet por una u otra según distintos criterios.
6.1 Generalidades
De modo predeterminado, linux está configurado para rechazar cualquier paquete cuyo destino no sea la propia máquina:
docs.iescdl.es/~josem/redes.html 14/49
27-10-2019 Configuración de redes en debian
$ cat /proc/sys/net/ipv4/ip_forward
0
de modo que, si deseamos que encamine tráfico (que es tráfico ajeno), tenemos que alterar este comportamiento. Para ello debe cambiarse el contenido
de ip_forward pero, aunque vale para ello un simple echo:
no es el método más apropiado si queremos hacer permanentes los cambios, puesto que al reiniciar la máquina, volveremos a encontrar el 0.
Para modificar valores de comportamiento dentro de la jerarquía /proc/sys, lo mejor es recurrir al fichero /etc/sysctl.conf, donde pueden
declararse variables y valores que, gracias al comando sysctl, se asignarán durante el proceso de arranque. En concreto, para el caso que nos ocupa,
la variable ya está declarada y tan sólo debemos descomentar la línea:
net.ipv4.ip_forward=1
# sysctl -p
Con esto tendremos preparado nuestra máquina para que el tráfico no dirido a ella, pueda circular libremente. No volverá a citarse de nuevo la
obligación de realizar este cambio, así que es importante entender que en todas las instrucción que se den a partir de ahora, se presupone que se ha
realizado esta operación. Ahora toca indicar cómo elegir la interfaz adecuada. Para ello, cualquiera familiarizado con estos temás sabe que existen las
tablas de encaminamiento. Supongamos este esquema de red:
En el que hay tres redes locales (e internet) y tres routers que las unen. El problema presentado es trivial y cualquier estudiante de redes mediamente
aplicado sabrá resolverlo sin mucho esfuerzo, pero es útil tomarlo como punto de partida para entender cómo tratar el encaminamiento en linux. Del
dicho al hecho, plasmemos la tabla de encaminamiento del segundo router etiquetato de R2:
La tabla sobredicha resuelve el encaminamiento del router 2, aunque no se han desglosado las entradas necesarias para el bucle local y el broadcast.
Obsérvese que, aunque las tablas suelen escribirse así, indicar la interfaz de salida es absolutamente gratuito, puesto que cuál es se puede deducir a
partir del valor de la puerta de enlace. En linux, en cambio, no existe una única tabla, sino varias que se van consultando según un determinado orden y
según un determinado criterio. Si consultamos el fichero /etc/iproute2/rt_tables, podremos ver lo siguiente:
O sea, cuatro tablas distintas (local, main, default y unspec) cada una con un byte identificativo único. De hecho, para referirnos a una tabla, podemos
usar tanto su nombre como su identificador. Estas son las tablas definidas inicialmente y cada una tiene una utilidad precisa:
local
Registra las entradas para el bucle local y el broadcast. Estas entradas son automáticas y no tendremos que manipularlas nunca.
main
Esta es la tabla en la que se almacenan de modo predeterminado las entradas para las redes conectadas (que se añaden automáticamente al
configurar las interfaces) y las entradas para redes no conectadas, las cuales hay que añadir a mano (o esperar a que se añadan, si hay
habilitados protocolos como RIP).
default
Vacía.
unspec
docs.iescdl.es/~josem/redes.html 15/49
27-10-2019 Configuración de redes en debian
Es una tabla especial de resumen: si consultamos su contenido, nos listará las entradas contenidas en las restantes tablas.
Ahora bien, si hay varias, ¿cómo se aplican? La respuesta está en la salida del comando:
$ ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
El primer número indica el orden de aplicación y el from all significa que la tabla se aplicará a todo paquete venga de donde provenga. Por lo tanto, si
se debe encaminar un paquete se comprueban las entradas de la tabla local, si no se logra encaminar el paquete se prueban las entradas de main y, si no
hay éxito, se intenta con las de la tabla default. En la práctica, la tabla local no la tocamos y es automática, la tabla default está vacía y si no le
indicamos a nuestra herramienta de consulta qué tabla queremos consultar se nos muestra main, así que en muchísimos casos podríamos vivir pensando
que en linux no existe más que una tabla de encaminamiento (main) y que linux nos ahorra leer las molestas entradas de bucle local y de broadcast. Por
supuesto, estas reglas de aplicación pueden modificarse (y de hecho, se hará cuando se desee gestionar varias salidas a internet), pero para un
encaminamiento normal no es necesario en absoluto.
Nosotros olvidaremos bajo los próximos dos epígrafes todo esto, y sólo lo retomaremos en el último, cuando sea necesario tenerlo presente.
Las entradas necesarias para las redes directamente conectadas se añaden automáticamente:
# ip route show
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1
de modo que no hay que preocuparse por ellas. Sin embargo, aún debemos indicar cómo salir a internet y esto hay que hacerlo explícitamente:
En este caso, que añadimos la salida predeterminada, puede también usar la palabra default:
# ip route show
default via 192.168.0.1 dev eth0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1
Aunque con esto habríamos completado la tarea de completar el encaminamiento, es muy común que, además, de encaminar debamos enmascarar el
tráfico de salida. Si observamos el esquema propuesto y pensamos en las máquinas que componen la red 192.168.0.0/24 (incluido el propio router
R1), llegaríamos a la conclusión de que, para que pudieran responder a cualquier comunicación procedente de la red interna 192.168.1.0/24, sería
necesario que supieran que han de enviar los paquetes hacia 192.168.0.2 y no hacia 192.168.0.1, que sería lo natural pues es su puerta de
enlace predeterminada. Así pues, habría que incluir una entrada en la tabla de encaminamiento de todas estas máquinas o al menos en el router R1 para
indicar que la puerta de enlace para la red 192.168.1.0/24 es la interfaz eth0 el router R2. Esto en algunos casos, puede llegar a ser imposible,
porque no tengamos permisos para obrar este cambio. Sin embargo, si nuestra intención es simplemente que las máquinas de la red interna se puedan
comunicar con el exterior del router R2, basta con que enmascaremos los paquetes a su salida por la interfaz eth0 del router R2. Esto consiste en
modificar la dirección de origen de los paquetes de manera que se sustituya la original por la de la interfaz de salida, en este caso, 192.168.0.2. De
esta forma, las máquinas externas pensarán que se comunican con el router R2, al cual saben llegar porque está en su propia red, y mandarán a él
sus respuestas. El router, por su parte, es capaz de distinguir las respuestas de este tráfico que enmascaró y saber que estos paquetes no son realmente
para él sino para el emisor original, con que modificará automáticamente la dirección de destino y lo enviará por la red interna hacia su legítimo emisor.
Esto tan largo de explicar se logra mediante un comando de iptables:
docs.iescdl.es/~josem/redes.html 16/49
27-10-2019 Configuración de redes en debian
En el caso de que eth0 recibiera por dhcp su ip y esta fuera cambiante, entonces es más recomendable dejar que iptables calcule en cada
momento la ip de la interfaz:
Si queremos que funcione, debemos indicarle a nuestra máquina que envíe los paquetes dirigidos a la red 192.168.2.0/24 en la interfaz con ip
192.168.1.2. Por tanto:
# ip route show
default via 192.168.0.1 dev eth0
192.168.2.0/24 via 192.168.1.2 dev eth1
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1
OBSOLETO
Tradicionalmente para añadir interfaces de red, se ha usado el comando route. Por ejemplo, la última entrada podría haberse añadido así:
Si quisiéramos usar ifupdown para configurar el esquema sencillo, deberíamos escribir en el ficheroç interfaces lo siguiente:
allow-hotplug eth0
iface eth0 inet static
address 192.168.0.2
gateway 192.168.0.1
allow-hotplug eth1
iface eth1 inet static
address 192.168.1.1
allow-hotplug eth0
iface eth0 inet static
address 192.168.0.2
gateway 192.168.0.1
up iptables -t nat -A POSTROUTING -o $IFACE -j SNAT --to-source ${IF_ADDRESS}
down iptables -t nat -D POSTROUTING -o $IFACE -j SNAT --to-source ${IF_ADDRESS}
Para el esquema más complejo que requiere la entrada adicional, deberíamos añadir opciones up y down a la declaración de eth1:
allow-hotplug eth1
iface eth1 inet static
address 192.168.1.1
up ip route add 192.168.2.0/24 via 192.168.1.2
down ip route del 192.168.2.0/24
docs.iescdl.es/~josem/redes.html 17/49
27-10-2019 Configuración de redes en debian
El problema en este caso consiste en que se tienen varios caminos para acceder a internet, pero no se desea agregarlos, sino mantenerlos
independientes, de modo que se escoja uno u otro camino atendiendo a distintos criterios. Por regla general, el criterio para determinar qué camino se
escoge para llegar a un destino es la dirección de destino del paquete. En este caso, sin embargo, hay dos (o más) puertas de enlace para alcanzar el
mismo destino, por lo que es necesario establecer criterios adicionales. Referido a esto existen dos términos informáticos:
Source-based routing
esto es, encaminamiento basado en el origen del paquete, lo cual significa que el camino se escoge dependiendo de cuál sea la red de origen
del paquete.
Policy-based routing
esto es, enrutamiento basado en políticas, que es una generalización del anterior término puesto que la elección se hace atendiendo a cualquier
criterio de red que deseemos establecer. Por ejemplo, podríamos desear que todo el tráfico de navegación saliera por una interfaz y que todo el
tráfico de correo electrónico saliera por otra. En este caso, el criterio sería el puerto de destino (80 y 443 para el primer tipo de tráfico y 25
para el otro).
Abordar esto, exige hacer memoria de qué en linux existen varias tablas de encamiento y conocer cómo crear nuevas y manipular las reglas para usar
unas u otras. Expliquémoslo resolviendo el siguiente supuesto:
Red Interfaz
ID
Nombre Dirección Máquina Nombre Dirección
1 Router 1 eth0 172.22.0.1
Red externa 1 172.22.0.0/16
2 Servidor eth0 172.22.0.2
3 Router 2 eth0 10.53.0.1
Red externa 2 10.53.0.0/27
4 Servidor eth1 10.53.0.2
5 Red interna 1 192.168.0.0/24 Servidor eth2 192.168.0.1
6 Red interna 2 192.168.1.0/24 Servidor eth3 192.168.1.1
Nuestra intención es que la red interna 192.168.0.0/24 salga por eth0, mientras que la red 192.168.1.0/24 lo haga por eth1, para lo cual no
vamos a seguir exactamente lo sugerido por la guía LARTC, que es un poco engorroso. La idea está tomada de Trent W. Buck, aunque tampoco se
implementa exactamente su solución.
Si volvemos a echar un vistazo a las entradas de una tabla de encaminamiento, comprobaremos que el único criterio en esas entradas para decidir la
puerta de enlace es la red de destino. Sin embargo, nosotros deseamos, aparte de este criterio, elegirla basándonos en el origen del paquete, puesto que
el destino siempre es el mismo: 0.0.0.0/0. La solución se halla en que podemos definir varias tablas de encaminamiento y definir reglas para usar
una u otra.
Partamos de que hemos configurado las cuatro interfaces y de que la dirección de loopback ya esté configurada:
# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
de las cuales local está rellena y es la primera que se revisa y main contiene las entradas para las redes directamente conectadas. Si en este punto
definiéramos una puerta de enlace para internet y la incluyéramos en la tabla main no podríamos especificar el origen del paquete, así que lo que
hacemos es lo siguiente:
# ip route show
throw default
10.53.0.0/27 dev eth1 proto kernel scope link src 10.53.0.2
172.22.0.0/16 dev eth0 proto kernel scope link src 172.22.0.2
docs.iescdl.es/~josem/redes.html 18/49
27-10-2019 Configuración de redes en debian
192.168.0.0/24 dev eth2 proto kernel scope link src 192.168.0.1
192.168.2.0/24 dev eth3 proto kernel scope link src 192.168.2.1
¿Qué significa este throw default? Básicamente significa que en esta tabla no se toma ninguna decisión de encaminamiento sobre cómo llegar a la
red 0.0.0.0/0 o default (en la orden hemos usado la palabra default, pero podríamos haber expresado explícitamente la red), sino que se debe regresar a
la política de rutas (la que muestra el comando ip rule) y probar con la siguiente tabla de encaminamiento apropiada. En realidad, este
comportamiento de continuar con la siguiente tabla es el predeterminado, así que podríamos habernos ahorrado el comando. El caso es que tal y como
está ahora mismo la política de rutas, no nos vale, sino que la tendremos que dejar de esta guisa:
Esta política de rutas supone que sucesivamente y hasta que se logre encaminar el paquete, se comprueba:
1. La tabla local.
2. La tabla main, donde se encuentran las entradas para las redes directamente conectadas.
3. Las dos reglas que hay a continuación permiten responder siempre por la interfaz que se recibió un paquete, es decir, si se recibió un paquete por
eth0, se responderá a él por eth0; y si por eth1, por eth1. Esto funciona así, porque si una aplicación de nuestra máquina origina, la ip de
origen se establecerá dependiendo de por qué interfaz salga. Sin embargo, si lo que hace la aplicación es responder a un paquete, la ip de origen
del paquete de respuesta, será la ip de destino que tuviera tal paquete (172.22.0.2 o 10.53.0.2); y, gracias a nuestras reglas 249 y 250,
nos aseguraremos de que salga por la interfaz que entró.
4. La tabla ADSL, pero sólo para el tráfico procedente de la red conectada a eth2. En esta tabla sólo incluiremos una entrada en que se indique que
la puerta predeterminada es 172.22.0.1. La consecuencia es que todo el tráfico de tránsito que entre por eth2 saldrá por eth0.
5. La tabla TIC es análoga a la tabla anterior, pero para la otra red interna: sólo conmprueban su entrada los paquetes procedentes de la red
conectada a eth3 y sólo habrá una entrada que lleve los paquetes 10.53.0.1.
6. La tabla default, por último, encamina todo lo que no haya sido encaminado hasta ahora. Por ejemplo, el tráfico que origina nuestra máquina.
Obsérvese que ADSL y TIC son dos tablas nuevas, cuyos nombres habrá que definir en el fichero adecuado:
Sobre esto cabe una puntualización: lo que hemos hecho simplemente es darle un nombre a las tablas, de manera que la tabla 100 se llamará ADSL y
así nos podremos referir a ella con un nombre legible fácilmente recordable. Sin embargo, podemos saltar este paso y referirnos siempre a las tablas con
su número identificativo.
Ahora sí, podemos redefinimos las políticas tal como las hemos propuesto:
Por último, nuestra intención es que una tabla tenga como salida predeterminada la 172.22.0.1 y la otra la 10.53.0.1; y que la tabla default use,
por ejemplo, 172.22.0.1:
Ahora bien, ¿qué debemos hacer si nuestra intención es hacer un encaminamiento basado en políticas? Por ejemplo, supongamos que proceda de
donde proceda el tráfico, queremos que el tráfico ICMP siempre salga a través de eth1.
La forma de dar solución a esto es marcar el tráfico, puesto que la política de rutas permite, además de indicar la procedencia, hacer referencia a la
marca del paquete. Así que empecemos por marcar el tráfico que nos interesa:
Y ahora añadamos una regla a la política de rutas que haga que este tráfico use la tabla TIC:
Obviamente, esta regla debe ser anterior a las que determinan la salida según la procedencia, pero posterior a las que nos permiten responder por la
misma interfaz que hemos escuchado. Con esto debería estar todo completamente configurado, pero puede no ser así. Dependiendo de la configuración
predeterminada para los parámetros del núcleo, podría ser que estuviera activada la defensa contra ataques de envenenamiento. Esta defensa consiste en
comprobar que según las reglas de encaminamiento un paquete procedente de una interfaz debería haber llegado efectivamente por esa interfaz. En esta
comprobación no se tienen en cuenta posibles marcas que pudiera tener el paquete y que modificaran la interfaz de comunicación, por lo que si usamos
el marcado para elegir salida, el cálculo de comprobación puede dar otra intefaz distinta y el paquete ser rechazado. Para que no se realicen estas
comprobaciones contra envenenamientos debemos cerciorarnos de que la opción /proc/sys/net/ipv4/conf/<interfaz>/rp_filter esté
a 0. Podemos confirmar si es así y, si vemos que no, podemos comprobar el contenido de /etc/sysctl.conf para asegurarnos de que contenga las
líneas:
docs.iescdl.es/~josem/redes.html 19/49
27-10-2019 Configuración de redes en debian
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
La segunda de estas líneas provocaría que al crearse una interfaz su parámetro rp_filter estuviera a 0.
DESACONSEJADO
Si queremos gestionar esta situación con ifupdown, no tenemos más remedio que crear un script que lo permita:
#!/bin/bash
#
# SOURCE-BASED ROUTING:
# Si a un destino se puede acceder por varias puertas de enlace, se
# realiza la elección en función de cuál sea la IP de origen del paquete.
#
# POLICY-BASED ROUTING:
# La elección se hace atendiendo a criterios más variados.
# Para ello se marca el tráfico con iptables (-j MARK) y se usa fwmark
# en "ip rule".
#
# MÉTODO DE IMPLEMENTACIÓN:
# Consiste en definir tantas tablas como combinaciones de puertas de enlace
# queramos definir para la salida. Por ejemplo, si hay dos puertas de enlace
# y queremos definir tres tipos de salida (todo sale por la puerta1, todo
# sale por la puerta2, y la mitad sale por la puerta 1 y la mitad por la 2),
# deberemos crear tres tablas dentro de las cuales se encontrará únicamente
# una entrada para la red "default" que defina exactamente así la salida.
# Es una ampliación de lo expuesto en:
# http://www.cyber.com.au/~twb/doc/dual-uplink.txt
#
# Vocabulario:
#
# 1. Interfaz de salida: Interfaz que se encuentra en una red con una
# puerta de enlace que comunica con internet.
# 2. Interfaz interna: Interfaz que no es de salida.
#
# Tablas predefinidas:
# cd /etc/network
# mv /donde/se/encuentre/multirouting.txt if-up.d/multirouting
# ln -s ../if-up.d/multirouting if-down.d/
Con este script el caso anterior quedaría resuelto añadiendo estas líneas a /etc/network/interfaces:
auto lo
iface lo inet loopback
multi_mark 0x1 TIC
allow-hotplug eth0
address 172.22.0.2
gateway 172.22.0.1
multi_alias ADSL
allow-hotplug eth1
address 10.53.0.2/27
multi_gateway 10.53.0.1
multi_alias TIC
allow-hotplug eth2
address 192.168.0.1
multi_default ADSL
allow-hotplug eth3
address 192.168.1.1
multi_default TIC
El script pretende resolver casos más complejos del expuesto aquí y que tienen que ver con el balanceo de carga, que se verá a continuación. Una
explicación prolija de cómo usarlo se encuentra en su propio código, incluidos cinco ejemplos de uso. Baste aquí indicar que:
docs.iescdl.es/~josem/redes.html 20/49
27-10-2019 Configuración de redes en debian
Ha de aclararse que ambas interfaces (y ambas puertas de enlace) se encuentran en redes distintas y que, por tanto, la solución requiere instrumentos de
capa de red. Si se pretendiera agregar interfaces en capa de enlace (lo cual significaría que ambas puertas de enlace se encontrarían en la misma red),
entonces la solución sería distinta y habría que recurrir al módulo bonding del núcleo de linux para crear una única interfaz virtual.
La configuración es muy semejante a la anterior, así que en vez de referirla por completo, mostraremos cuáles son las diferencias. En primer lugar, si
vemos nuestra política de rutas:
Sobran las reglas que encaminan por una puerta de enlace determinada las redes internas, puesto que nuestro objetivo es balancear:
Con esta configuración todas esas comunicaciones (y las que origina el propio servidor) acaban en la tabla default, que encamina según nuestra
configuración anterior, o sea, siempre a través de eth0
Pues bien, es esta entrada la que hay que modificar para producir el balanceo:
# ip route replace default table default nexthop via 172.22.0.1 weight 2 nexthop via 10.53.0.1 weight 1
En el ejemplo se le ha dado el doble de peso a una puerta de enlace que a la otra, por lo que saldrá el doble de tráfico por una que por otra. La tabla
debe quedar así:
Nótese que todo esto no es incompatible con que haya tráfico marcado o procedente de una red interna que siempre salga por una interfaz: todo es
cuestión de incluir reglas en la política de rutas que hagan salir este tráfico a través de una tabla en particular.
DESACONSEJADO
Para gestionar este caso con ifupdown puede hacerse uso del script anterior que está preparado también para ello. La configuración quedaría así:
allow-hotplug eth0
address 172.22.0.2
gateway 172.22.0.1
multi_table default 2
allow-hotplug eth1
address 10.53.0.2/27
multi_gateway 10.53.0.1
multi_table default
allow-hotplug eth2
address 192.168.0.1
allow-hotplug eth3
address 192.168.1.1
Aunque gateway incluye en la tabla default la puerta de enlace 172.22.0.1 lo hace con peso 1, así que para indicar el peso nos vemos
obligados a usar multi_table que hace participar la puerta de enlace en la tabla expresada (default en este caso).
En el caso de la otra puerta de enlace, nos vemos obligados a usar multi_table, porque multi_gateway no implica que se incluya en la
tabla default. Al no indicar peso, se sobreentiende 1.
Las redes internas no tienen definida la opción multi_default, porque se quiere que usen la salida definida a través de la tabla default,
que es la que se usa para aquel tráfico que no tiene especificada su salida.
NOTA Para la configuración de encaminamientos múltiples a través de ifupdown existe el paquete de debian ifupdown-multi: parece una buena
alternativa a este script, aunque el autor no lo ha probado.
Ya se han visto dos técnicas para unir dos partes de una LAN:
docs.iescdl.es/~josem/redes.html 21/49
27-10-2019 Configuración de redes en debian
En el primer caso los dispositivos de una parte que quieren comunicarse con la otra han de enviar sus paquetes a la ip de la interfaz del router que cae
en su misma red. Esta ip es lo que se conoce como puerta de enlace.
En el segundo caso, la presencia del puente es totalmente transparente y los paquetes de una y otra parte lo atraviesan con total libertad. De hecho, ni
siquiera es necesario que la interfaz bridge posea una dirección ip. A ojos de un cliente es indistingible un cliente se haya en el mismo segmento de red
que en el otro.
Pero hay una tercera vía para conectar ambas partes y que es una solución intermedia entre las dos anteriores: el proxy ARP. Es intermedia en la
medida en que ambas partes se encontrarán dentro de una misma red, lo cual lo asemeja a la segunda vía; y en que la máquina intermediaria dispondrá
de dos direcciones ip (una para cada interfaz) que harán las veces de puerta de enlace (ya matizaremos esto), lo cual lo asemeja a la primera. En
realidad, tiene más de la primera que de la segunda, ya que el tráfico es encaminado entre ambas interfaces y no conmutado, como en el caso de un
bridge.
Un proxy ARP manipula las peticiones ARP de los clientes, de modo que si le llega una procedente de un cliente que pregunta por la MAC de otro
cliente del otro lado, responderá devolviéndole la dirección MAC de la interfaz que tiene en el lado de la petición. Si se observa la figura del ejemplo
que lo ilustra, la tabla ARP del cliente con ip 192.168.0.100, tendría un aspecto como este:
$ ip n
192.168.0.1 dev eth0 lladdr de:ad:be:ef:00:01 STALE
192.168.0.15 dev eth0 lladdr de:ad:be:ef:00:0f STALE
192.168.0.30 dev eth0 lladdr de:ad:be:ef:00:1e STALE
.
.
.
192.168.0.200 dev eth0 lladdr de:ad:be:ef:00:01 STALE
192.168.0.201 dev eth0 lladdr de:ad:be:ef:00:01 STALE
O sea, de todas las máquinas de su segmento conocería la mac real, pero de las máquinas del segmento opuesto pensaría que la mac es la de la interfaz
eth1 del servidor, puesto que éste, cuando recibe la petición ARP, la falsea y responde con su propia mac. De este modo, cuando el cliente pretenda
comunicarse con el cliente del lado opuesto (192.168.0.200), el paquete será recibido por la interfaz eth1 del servidor, puesto que contendrá su
mac, y éste lo enviará por la otra interfaz hasta su destino.
Usar esta estrategia para dividir la red tiene una serie de ventajas e inconvenientes:
7.2 Configuración
Debe colocarse una ip a cada interfaz del proxy:
# sysctl -w net.ipv4.ip_forward=1
# sysctl -w net.ipv4.conf.eth1.proxy_arp=1
# sysctl -w net.ipv4.conf.eth2.proxy_arp=1
Con esto basta: ambas partes podrán comunicarse del modo descrito.
docs.iescdl.es/~josem/redes.html 22/49
27-10-2019 Configuración de redes en debian
En este caso particular, se podríam haber hecho estas asignaciones a las interfaces:
y ni siquiera habría sido necesario añadir las dos rutas. Además, si dispusiera un servidor dhcp, sería posible entregar direcciones del rango adecuado
por cada interfaz. De todos modos, si fuera posible hacer esta división, sería mejor dividir en dos subredes.
NOTA POR ESCRIBIR (aunque mejor en dhcp: cómo se configura el servidor dhcp). Ver la opción use-lease-addr-for-default-route true;
Si llegamos a implementar algunos de los usos cuyas soluciones se proponen en este documento y, además, instalamos algunos servicios (web, DNS,
SSH, etc.) nos encontraremos con un servidor que sirve para encauzar clientes de redes internas hacia internet y que ofrece servicios al exterior. Esto
puede llegar a suponer un gran consumo de ancho de banda, sobre todo a determinadas horas. Dadas las limitaciones de conexión a internet esto se
puede traducir en un deficiente servicio, tanto a los usuarios internos que intentan acceder al exterior, como a los usuarios externos que intentan hacer
uso de alguno de nuestros servicios desde sus casas.
La solución a este problema, aparte de contratar un mayor ancho de banda que a veces es la única posible, puede venir de la mano de lo que se llama
control de tráfico y, en otras ocasiones, calidad de servicio. Si no configuramos nada al respecto, la salida de los paquetes por la interfaz que conduce
hacia internet es una cola FIFO (en realidad, es un pelín más complicado como se discutirá más adelante), en la que el primer paquete que llega a la
cola es el primero que sale. No hay distingos entre los distintos tipos de tráfico, así que puede darse el caso de que una conexión que consuma gran
ancho de banda (p.e. un usuario externo que descarga un fichero medianamente grande que hemos colocado en el servidor web) impida que el
administrador pueda usar con comodidad SSH para realizar alguna configuración importante en el servidor: éste notará tirones en la conexión y que
durante algunos segundos es incapaz de escribir nada en la terminal, para que al cabo de este tiempo se escriban de sopetón todas las letras que intentó
teclear.
El control de tráfico permite priorizar ciertas conexiones o reservarles un ancho mínimo, aun cuando otras personas estén haciendo un uso intensivo del
ancho de bando.
Una planificación de tráfico o disciplina de cola (queue discipline en inglés) es el algoritmo que se fija para determinar de qué modo salen o entran los
paquetes por una interfaz. Ya hemos dado un ejemplo de un algoritmo muy sencillo, el FIFO, que va poniendo en cola los paquetes y haciéndolos salir
por la interfaz según el orden en le que llegaron.
Obviamente todas las interfaces tienen que tener definido una planificación, porque el kernel debe saber cómo hace salir los paquetes por ella. Cuando
no se establece ninguno en particular, linux le asigna a la interfaz la qdisc PFIFO_FAST, que es una variante del FIFO:
Esta es la planificación para la salida (root) que existe de forma predeterminada. Puede, por supuesto, ser cambiada y todo este apartado trata de cómo
hacerse y con cuáles otras planificaciones hacerse.
Hay dos tipos de algoritmos: los algoritmos sin clases y los algoritmos con clases. Los segundos permiten clasificar los paquetes según distintos
criterios y establecer distintas colas para los distintos tipos de tráfico que se establezcan.
Antes de comenzar, no obstante, es importante entender correctamente el alcance y la eficacia de una planificación. Entiéndase que el único tráfico que
realmente podemos controlar es el tráfico de salida, ya que lo generaremos en el propio servidor o en otra máquina local que también podremos
manipular. El de entrada, sin embargo, si procede de internet, sólo puede tratarse al llegar a la entrada de la interfaz, de manera que lo que haya pasado
antes con el paquete nos es impòsible de controlar: ni podremos asegurar con que ToS nos llega el paquete ni cómo planificará su salida el router del
ISP, etc. Lo único realmente eficaz que podremos hacer es descartar tráfico. Por ello, la planificación de entrada (ingress) es sumamente simple,
aunque existan argucias para crear sobre ella planificaciones complejas. Se tratará el control de la entrada más adelante.
Son las más sencillas. Revisaremos tres, la predeterminada PFIFO_FAST, la SFQ y la TBF, cuyos principios son muy útiles para entender la
planificacione con clases HTB.
8.2.1.1 PFIFO_FAST
Es una variante algo más sofisticada del algoritmo FIFO. Su esquema es el siguiente:
docs.iescdl.es/~josem/redes.html 23/49
27-10-2019 Configuración de redes en debian
Según llegan los paquetes a la interfaz el kernel comprueba el campo ToS de la cabecera IP y dependiendo del valor clasifica el
paquete en tres bandas de mayor (0) a menor (2) prioridad. Las bandas funcionan de forma que hasta que una banda de mayor
prioridad no se ha vaciado, no se comienza a vaciar la siguiente en prioridad. Cada banda se vacía según el principio FIFO.
Para conocer cómo se analizan los paquetes y clasificarlos es necesario profundizar un poco más en el byte ToS (según la definición
antigua del RFC 791):
Los tres primeros bits indican la prioridad del paquete, pero en la práctica no han sido usados. El último bit siempre vale 0 y los bits
3-6 son propiamente los que indican el tipo de servicio:
Como el séptimo bit siempre es 0, para calcular el valor del ToS, hay que doblar el valor decimal. Sabido esto podemos desglosar
cómo calcula linux la prioridad y a qué banda asigna dicha prioridad:
Como vemos, a partir del ToS el kernel asigna una prioridad y esta prioridad determina la banda (0, 1 ó 2) que se asigna al paquete.
La asignación de banda la determina el valor del campo priomap que en esta planificación es fijo: 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1,
1, 1, 1. Entiéndase:
Prioridad linux 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Banda 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
... los paquetes con prioridad 0 van a la banda 1; los paquetes con prioridad 1, a la banda 2, etc. Las prioridades mayores a 6 se
establecen mirando otros bits del byte, dado que aquí se ha discutido un uso antiguo que sólo utilizaba en la práctica cuatro bits. Más
docs.iescdl.es/~josem/redes.html 24/49
27-10-2019 Configuración de redes en debian
adelante se discutirá algo del uso más moderno.
Cada tipo de conexión tiene sus bits de ToS fijados atendiendo a su naturaleza. Algunas son estas:
La última columna es el resultado de aplicar la máscara 00011100 (0x1c en hexadecimal) al byte. Este resultado es útil, porque el
RFC 2474 redefinió el byte por completo introduciendo el DSCP para los seis primeros bits y el RFC 3168 el ECN (Notificación de
Congestión explícita) para los dos últimos. Esto hace que dependiendo de qué se use para codificar el tipo de servicio, el valor del
byte pueda ser uno u otro:
No obstante, los bits 3-5 coinciden y, si en alguna otra circunstancia tuviéramos que identificar tráfico, lo podríamos hacer mirando
exclusivamente estos bits (máscara 0x1c).
Por otro lado el tamaño de las bandas viene determinado por el parámetro tx_queue_len:
$ cat /sys/class/net/eth0/tx_queue_len
1000
que puede definirse para cada interfaz con el comando ip (véase man ip-set). Cuando se supera el tamaño de la cola, se comienzan
a desechar paquetes, aunque el protocolo TCP debería ajustar la velocidad de envío a la velocidad del medio y que esto no ocurriera.
8.2.1.2 SFQ
A diferencia de la planificación anterior en esta no se prioriza ningún tipo de tráfico. La estrategia consiste en generar un hash de
cada paquete a partir de su información de cabecera (por ejemplo, ips y puertos de origen y destino) y en función del valor enviar los
paquetes a una de las 1024 cubetas disponibles, las cuales se van vaciando siguiendo el algoritmo round-robin, o sea, de la primera
se extrae un paquete, de la segunda otro y así sucesivamente hasta llegar a la última, para luego volver a empezar.
Como pudiera ocurrir que unas cubetas se llenaran más rápido que otras, cada cierto tiempo se modifica el algoritmo de hash.
docs.iescdl.es/~josem/redes.html 25/49
27-10-2019 Configuración de redes en debian
quantum: máximo número de bytes que salen de una cubeta en cada turno. Debe ser como mínimo el MTU, que para redes
Ethernet suele ser 1500 bytes. De este modo, saldría un paquete por turno.
Esta planificación es adecuada para balancear tráfico de manera que ninguno resulte beneficiado. Obviamente, si se pretende definir
una sóla planificación en la interfaz de salida, la planificación anterior es más apropiada, ya que favorece el tráfico de mayor
prioridad. Esta, en cambio, puede ser útil en otros casos. Por ejemplo, imagínese que tenemos un router como el R2:
que encamina todo el tráfico de la RED 2. Si no hemos tocado la planificación de su interfaz eth0, esta será una PFIFO_LAST, lo
cual no está del todo mal, ya que prioriza según la prioridad del tráfico. Ahora bien, dentro de cada una de sus bandas (en que la
prioridad es la misma), no se hace distingos en el tráfico, ya que los paquetes son evacuados mediante un algoritmo FIFO sin más.
Así, pues, un cliente interno podría copar el tráfico. Sería más lógico que dentro de cada una de estas bandas se pudiera repartir
equitativamente la salida según clientes y tráfico: así ninguno salía beneficiado. Este sería un caso en que SFQ es útil.
Desgraciadamente, PFIFO_FAST no deja hacerlo, pero otra planificación como PRIO sí.
Pero ya se ha advertido que como planificación raíz sobre la interfaz no es muy adecuado. Si consultamos la planificación:
Son reconocibles los parámetros que se han discutidos. Nótese también lo remarcado: 8001:. Este es un handle que identifica a la
planificación y cuyo formato son dos números separados por dos puntos. El segundo, al no aparecer, se supone 0. Por convenio, a las
planificaciones raiz se les suele asignar el handle 1: (o 1:0, si se prefiere). Por tanto, habría estado mejor hacer:
dentro. por supuesto, de la inconveniencia que supone usar esta planificación. Puede usarse cualquier handle, aunque el ffff: está
reservado para la planificación de entrada (ingress).
8.2.1.3 TBF
Esta planificación es interesante porque permite introducir los conceptos que se encontrarán en HTB. A diferencia de los algoritmos
anteriores permite controlar la velocidad a la que saldrán los paquetes, para lo cual se usa una argucia basada en tokens o fichas, si se
prefiere. Para entender el mecanismo podemos usar un símil hidráulico. Supongamos que disponemos de un bidón en el que
almacenamos agua que se llena por la parte superior con un determinado caudal constante, y se vacía por la parte inferior mediante
un grifo, cuyo caudal puede ser mucho mayor que el de entrada:
En estas condiciones es obvio que aunque abramos al máximo el grifo inferior, el caudal medio de desagüe siempre sera igual al
caudal constante de entrada. Ahora bien, como disponemos de un bidón que almacena el agua, si cerramos el grifo inferior durante
un rato, de manera que dejamos que el bidón se llene en parte o totalmente, cuando abramos el grifo, podremos desaguar al caudal
máximo que nos permita el grifo inferior hasta que tengamos el bidón completamente vacío.
Como en los algoritmos anteriores, vamos almacenando paquetes en una cola a espera de poder salir por la interfaz. La diferencia
ahora es que se manipula la salida de estos paquetes de la cola introduciendo un mecanismo de control de la velocidad a la que salen.
Para ello se dispone un bidon que en vez de llenarse de agua, se llena de fichas por valor de 1 byte. La velocidad de llenado es lo que
se denomina rate en el esquema. Además hay una valocidad de vaciado máxima, llamada peakrate y una capacidad máxima del
bidón denominada burst. Existe un bidón más pequeño en donde desaguan las fichas del bidón anterior. Este bidón, por lo general es
del tamaño de la MTU por lo que sólo es capaz de albergar un paquete. Su capacidad de vaciado es teóricamente infinita, pero en la
práctica, dada su capacidad, desagüa a la velocidad a la que se llena, esto es, peakrate. Sacar de la cola un paquete de 1500 bytes
exige consumir 1500 fichas, así que si el flujo de paquetes es constante e impide que se llene el bidón sólo podremos sacar paquetes
a la velocidad que fije rate. Si por el contrario, durante un determinado tiempo no hay paquetes que enviar, el bidón se irá llenando,
de modo que, cuando los haya, podremos enviarlos a una velocidad máxima de peakrate hasta el máximo indicado por burst (la
capacidad del bidón).
Este concepto de burst es útil para tráfico que vaya a tirones. Por ejemplo, la navegación de un usuario que pide una página,
momento en el que hay tráfico, y luego se dedica un tiempo a ojearla, momento en el que no lo hay.
Como en la planificación anterior, se denomina limit a la cantidad de bytes en espera de ser enviados. Obsérvese que en condiciones
ideales, si se envían paquetes a la velocidad fijada por rate, los paquetes esperarán para ser enviados un tiempo igual a la división
limit/rate. Este tiempo medio de espera es lo que se denomina latencia y puede ser indicado al definir la planificación
alternativamente al de limit.
# tc qdisc add dev eth0 root tbf rate 1mbit burst 10k latency 50ms
Obsérvese que que no se ha definico la mtu, la cual se toma con la definida para la interfaz física (habitualmente en redes Ethernet
1500 bytes. Este es un valor adecuado si realmente los paquetes tienen ese tamaño, pero lo más probable es que al pasar por la
planificación no sea así. La culpa la tiene una tecnología llamada LSO. Sin ella, se supone que en la capa de transporte el protocolo
troceará los segmentos con un tamaño tal que al añadírsele las cabeceras ip y ethernet, los paquetes no alcancen el tamaño fijado por
la mtu. En redes de alto rendimiento, para ahorrar ciclos de procesador, los datos se segmentan en paquetes muchos más grandes que
al llegar a la interfaz son troceados por el hardware de la tarjeta para ajustarte al mtu. De hecho, si monitorizamos tráfico con
tcpdump comprobaremos que los paquetes son bastante grandes. La consecuencia de que esté habilitada esta tecnología es que el
mtu que tiene definida la planificación es demasiado pequeño y no nos van a funcionar. La solución es clara: o deshabilitamos esta
tecnología con ethtoll o fijamos un mtu mucho mayor:
# tc qdisc add dev eth0 root tbf rate 1mbit burst 128k mtu 64k latency 50ms
Obviamente esta estrategia por si sola es muy pobre, pues no permite distinguir tipos de tráfico: lo útil es tener varias colas TBF en
paralelo, de manera que a cada una vaya a parar un tipo de tráfico: así podremos establecer distintas ratios de envío según el tipo. De
eso trata, precisamente, la planificación HTB.
8.2.2.1 PRIO
Esta planifición es conceptualmente muy parecida a la PFIFO_FAST (en la guía LARTC la califican de PFIFO_FAST con
esteroides), pero con la particularidad de que permite configuración. Si se analiza la PFIFO_FAST con amplitud de miras, se podría
considerar de que dispone de clases (cada una de las tres bandas puede considerarse una), pero que estas ni son configurables en
número (siempre hay tres) ni son configurables en tipo de tráfico (a cada banda siempre va el mismo tipo de tráfico).
Pues bien, en esta planificación podemos indicar cuántas bandas queremos que haya y también que tipo de tráfico queremos que
vaya a cada banda. Esto segundo puede hacerse de dos formas:
1. Como en PFIFO_FAST, calculando la prioridad a partir del byte ToS y asignando una banda a cada prioridad mediante el
campo priomap (recordemos que en PFIFO_FAST este campo existe pero tiene un valor que no es modificable).
2. Mediante la creación de filtros que permiten asignar a las bandas cualquier tipo de tráfico a partir de la información que se
pueda encontrar en las cabeceras de los paquetes.
Para el envío de paquetes se usa la misma técnica que en PFIFO_FAST: hasta que una banda de mayor prioridad no se vacíe de
paquetes, no se envían paquetes de la siguiente banda. Por tanto, las clases (las bandas) no tienen asignado un ancho determinado de
banda.
docs.iescdl.es/~josem/redes.html 27/49
27-10-2019 Configuración de redes en debian
Para configurar esta planificación son necesarios fundamentalmente tres datos:
Por tanto, si en una interfaz se hace esta planificación sin especificar ningún parámetro, obtendremos una planificación
PFIFO_FAST sin más. Veámoslo:
Cómo aún no hemos establecido ninguna planificación sobre eth0 la que hay es una PFIFO_FAST. Establezcamos ahora una PRIO
sin especificar nada:
O sea, el mismo perro con distinto collar. Lo que sí es didáctico y, por tanto, particularmente útil es que con esta planificación
podemos desglosar cada una de las bandas: la 1:1 (que hace las veces de 0), la 1:2 (1) y la 1:3 (2). Gracias a ellos podremos
comprobar que la planificación PFIFO_FAST funciona como se explicó:
Como puede verse, sólo la banda 0 (1:1) ha tenido tráfico. Si ahora confieso que estoy haciendo pruebas sobre una máquina virtual
que manejo desde una sesión ssh, esto cobra sentido, ya que el tráfico ssh interactivo es tráfico de mínimo retardo y va a la banda 0,
según ya se vio. Probemos a enviar un fichero con scp, que según lo visto, debería ir a la banda 2 (la de menor prioridad):
Cosa que efectivamente sucede. Como hemos querido emular la planificación PFIFO_FAST, no hemos llegado más allá en la
configuración: nos hemos limitado a usar tres bandas, a usar el priomap predeterminado y no hemos definido ninguna planificación
final para ninguna de las tres bandas.
Sin embargo, podríamos haber cambiado el número de bandas, la asignación del tráfico a cada banda y haber especificado
planificaciones para ellas. Alterar el número de bandas es trivial: sólo hay que especificarlo al crear la planificación. Modificar la
asignación de tráfico a las bandas puede hacerse de dos formas:
Por último, la asignación de una planificación final a cada banda es trivial. Por ejemplo, pasa asignar una SFQ a cada una de ellas:
En este artículo se propone una planificación PRIO para resolver un sistema en el que hay VoIP: se crean dos bandas: la de mayor
prioridad para el tráfico VoIP y la de menor prioridad para el resto del tráfico. Para esta segunda banda se establece un planificación
HTB, que se verá posteriormente, a la que irá todo el resto del tráfico. En nuestro caso de implementación, se ha usado también esta
planificación para hacer que el tráfico de una subred penalizada tenga menor prioridad.
8.2.2.2 HTB
Esta planificación es una generalización de la TBF con clases, de manera que cada clase sigue una planificación TBF. Además
introduce algunos conceptos nuevos para mejorar el rendimiento:
docs.iescdl.es/~josem/redes.html 28/49
27-10-2019 Configuración de redes en debian
En esta estrategia unos filtros iniciales permiten enviar el tráfico a una y otra clase (en el esquema se han considerado dos). Estas
clases actuán según la planificación TBF con una modificación: una clase puede pedir pedir fichas a otra clase, si esta otra no las está
utilizando, de manera que podrá enviar paquetes a una velocidad máxima dada por su ceil. En cambio, en competencia con el resto
de las clases, enviaría paquetes a una velocidad dada por su rate, siempre y cuando se cumpla que la suma de las ratios de cada clase
sea igual a la velocidad máxima a la que puede enviar paquetes la interfaz.
burst: es el tamaño del bidón, como en la planificación anterior. Puede definirse para cada clase.
rate: es la velocidad de llenado del bidón, como en la planificación anterior. Puede definirse para cada clase.
ceil: es la velociad máxima a la que se pueden enviar datos pidiendo prestado parte de su rate a las otras clases. Puede
definirse para cada clase.
cburst: el tamaño del segundo bidón.
r2q: es la relación entre rate y el quantum tal y como se definió en la planificación SFQ, o dicho de otra forma, los quantum
que se atesoran por segundo. Para valores muy pequeños (menores de 15 kbit) o muy grandes de rate es útil redefinirlo.
Obsérvese que al estar relacionadas r2q, rate y el quantum, sólo dos de ellas son variables independientes. Como r2q vale 10 de
modo predeterminado, al crear la planificación HTB y definir en cada clase un rate, la variable calculada es quantum. Sin embargo,
si debido a su rate el quantum resulta muy pequeño o muy grande para una determinada clase, se puede definir particularmente para
ella un quantum.
Sobre la redistribución del ancho de banda sobrante de una de las clases entre las demás es importante tener en cuenta lo declarado
aquí: los tokens se redistribuyen de forma proporcional al quantum de cada clase. Si no se ha redefinido ninguno en particular,
resulta que el quantum de cada clase es proporcionales a su rate, por lo que en una redistribución de tokens sobrantes una clase con
el doble de rate que otra, recibirá el doble de tokens extra. A menos que al definir las clases se definan prioridades (con prio) en
ese caso, las clases con prioridad menor, recibirán tokens antes que las clases con prioridad mayor.
Cada clase, a su vez, puede dividirse en subclases o definirse para ella una planificación determinada (una SFQ por ejemplo).
En el apartado dedicado al caso práctico se tiene un ejemplo de cómo usar las planificaciones, las clases y los filtros, que se
discutirán a continuación.
Todo lo que se ha discutido aquí es de planificaciones para paquetes de dejan la interfaz (tráfico de salida), pero ¿qué ocurre con los paquetes
que entran por la interfaz? Para la interfaz puede definirse también una planificación:
para la que no se puede definir ningún parametro. Lo que sí se pueden añadir a ella son filtros y estos filtros a su vez pueden tener definidos
controles que pueden limitar tráfico como se hace en la planificación TBF. Por ejemplo:
# tc filter add dev eth0 parent ffff: protocol ip u32 match ip src 172.22.0.0/16 police rate 512kbit burst 128k mtu
Como puede observarse con police se han definido los parámetros propios de una planificación TBF. Como se ha añadido drop, esto
significa que todos los paquetes que excedan este ratio serán desechados. El significado de esta línea es que se permitiría un tráfico máximo
de entrada para paquetes procedentes de la red 172.22.0.0/16 de 512 Kbit/s.
Pueden añadirse varios filtros con distinta prioridad, de modo que se podrá limitar el ratio de entrada de distintos tráficos (aunque para
entenderlo será necesario estudiar antes los filtros). También es posible definir planificaciones de los tipos ya estudiados recurriendo a la
triquiñuela de de desviar el tráfico hacia una interfaz virtual ifb0 y tratar el tráfico de salida de esta interfaz. Sin embargo, todo esto es por lo
docs.iescdl.es/~josem/redes.html 29/49
27-10-2019 Configuración de redes en debian
general poco eficaz dado el nulo control que tenemos sobre el tráfico que recibimos. A la postre la mejor planificación de entrada es controlar
adecuadamente el tráfico de salida.
Para establecer este último control han de tenerse en cuenta al menos tres aspectos:
1. Los anchos de banda de subida y bajada que nos ofrece el ISP no son los efectivos de los que disfrutaremos a la salida y la entrada de la
interfaz externa del servidor, puesto que la necesaria encapsulación que se haga para conectar con el router del proveedor hace perder
algo.
2. Conviene no saturar el enlace, para impedir que el router del ISP ponga en cola muchos paquetes y se dispare la latencia. Esto se
traduce en desechar paquetes para que el tráfico de entrada no alcance el ancho de bajada.
3. En los servicios TCP en que fundamentalmente un cliente obtiene datos de un servidor (HTTP es un buen ejemplo), por cada paquete de
datos que el servidor envía al cliente, éste responde con un pequeño paquete que confirma la recepción. Por tanto, si disminuye el flujo
de paquetes de confirmación, disminuitá el flujo de datos que envíe el servidor. Esto puede sernos una herramienta muy poderosa para
gestionar el tráfico de entrada sin aplicar ninguna planificación directamente. Por ejemplo, si tenemos una serie de clientes internos que
se conectan a servidores web externos, actuar sobre sus pauqtes de confirmación, tiene un efecto directo sobre la velocidad con la que
bajarán los datos.
Bajo el próximo epígrafe se discutirán estos aspectos haciendo algunos números orientivos.
Si disponemos de una conexión ADSL o de fibra óptica es probable que se use ATM para transferir los datos y PPPoE. Estos protocolos
consumen parte del ancho de banda en sus cabeceras lo que implica una merma en los anchos de banda que publicita el operador. Un cálculo
estimativo puede hacerse a partir de la siguiente forma:
La cabecera PPPoE requiere 8 bytes. Como la MTU suele ser de 1500, tenemos una pérdida de 8 bytes cada 1500 (o sea, un 0,53%).
Cada celda ATM se compone de 53 bytes, de los cuales 48 son de datos. Esto, pues, supone una pérdida de 6 bytes cada 53. Si pasamos a
1500 bytes y redondeamos al alza, obtenemos un total de 174 bytes cada 1500.
En total, se pierden unos 153 bytes cada 1500, o lo que es lo mismo, un 10,2% de pérdidas.
Calculemos ahora la relación que existe entre un flujo de datos de descarga y el flujo de datos de confirmación que existe en los protocolos
que usan TCP en la capa de transporte. Los paquetes ACK de confirmación suelen tener un tamaño de 60 ó 70 bytes, mientras que los paquetes
de datos, si la descarga es prolongada tendrán casi todos ellos un tamaño cercano al MTU, o sea, 1500 bytes. Como previamente hay una
negociación en la que estos paquetes no son tan grandes y al final también podrá haber paquetes algo más pequeños, supongamos que el
tamaño medio de estos paquetes es de 1300 bytes. En estas condiciones, para cada paquete de 1300 bytes de descarga, existe un paquete de
confirmación de unos 65 bytes, lo cual significa que el tráfico de confirmación es aproximadamente un 5% del tráfico de descarga.
Este número, no obstante, es muy aproximado dependiendo del caso puede ser mayor o menor. Por ejemplo, si un único cliente descarga un
largo fichero de internet la relación baja a aproximadamente el 3.5%.
8.4 Filtros
Los filtros permiten clasificar los paquetes, o dicho de otro modo, colocarlos en distintas colas. tc provee distintos métodos de filtrado (o
clasificadores):
1. El protocolo RSVP, pero que sólo es útil si se tiene el control de toda la red, por lo que no sirve para controlar el tráfico hacia internet.
2. tcindex.
3. route, que se basa en clasificar el tráfico basándose en las tablas de encaminamiento.
4. fw, que se basa en observar la marca del paquete fijada por el cortafuegos.
5. u32, que se basa en observar directamente el paquete y tomar decisiones en base a lo que se encuentre en él.
6. ematch, el método más moderno y más verśatil, semejante al anterior, pero mucho más potente.
Dedicaremos este apartado fundamentalmente a explicar los dos últimos métodos, ya que exige el conocimiento de una sintaxis específica para la
escritura de los filtros. No obstante, también se expondrán fw y route, que no tienen excesiva complicación.
Los filtros pueden adjuntarse tanto a una qdisc como a una clase, ahora bien, para que se apliquen es necesario que el paquete la visite. Es
importante tener en cuenta esto, puesto que si adjuntamos un filtro a una clase que el paquete no visita nunca, entonces el filtro jamás se aplicará. A
menos que haya muchos filtros y nuestro control sea muy complejo y lo recomiende, lo más sencillo es es adjuntar los filtros a la root qdisc, ya que
todos los paquetes que pretendan salir por la interfaz llegan a ella.
Supongamos que tenemos la siguiente planificación para nuestra interfaz de salida eth0:
Como consecuencia de ellas, todo el tráfico del que no se diga nada al respecto, irá a la clase 1:20. Nuestra intención es crear filtros que hagan que el
tráfico que nosotros dispongamos, vaya a la clase 1:10. En este caso, un ejemplo de filtro tendría este aspecto:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 <filtro> flowid 1:10
Como vemos, se adjunta el filtro a la root qdisc de la interfaz eth0 con prioridad 1. Esta prioridad determina el orden en que se aplican los filtros,
si se dispone varios: cuanto menor sea la prioridad, antes intentará aplicarse. La parte que se ha notado vagamente como filtro será la expresión del
filtro, que ya veremos cómo se hace bajo los siguientes epígrafes. También hay que indicar, obviamente, hacia que clase se dirigirá todo paquete que
cumpla con el filtro. Se ha indicado además que el protocolo es ip, pero pospondremos la discusión para el final del epígrafe.
docs.iescdl.es/~josem/redes.html 30/49
27-10-2019 Configuración de redes en debian
Si queremos borrar el filtro basta con:
o bien, para ver las estadísticas de paquetes que han acabado en cada clase:
Finalmente, retomemos la discusión sobre el protocolo que se indica al añadir un filtro. Si observamos una cabecera Ethernet:
veremos que en el último campo de dos bytes (EtherType) se indica el tipo de protocolo que encapsula la trama (ip, arp, etc). Al decir, por ejemplo,
protocol ip, estamos diciéndole al comando que compruebe si ese campo tiene el valor 0x800, que es el valor para el protocolo IPv4. Si lo que
pretendiéramos es filtrar tráfico arp entonces deberíamos indicar protocol arp. Lo habitual, por tanto, es que siempre se indique que el protocolo es
el ip. Ahora bien, hay una excepción bastante común a esto: cuando se trabaja con tráfico etiquetado mediante el protocolo 802.1q. Cuando este es el
caso, entre la MAC de origen y el campo EtherType, se añaden cuatro bytes:
docs.iescdl.es/~josem/redes.html 31/49
27-10-2019 Configuración de redes en debian
Los dos primeros siempre son 0x8100, que indican que el protocolo de etiquetado es el 802.1q y de los otros dos los doce últimos bits son el identicador
de la VLAN. Luego, ya sí, se encuentra el campo EtherType con valor 0x800, si lo que se encapsula es un paquete ip. Por esta razón, cuando el
tráfico está etiquetado no puede indicarse protocol ip, puesto que donde debería haber 0x800 hay un 0x8100. En este caso es necesario indicar que
el protocolo es el 8021q. Por ejemplo, para filtrar tráfico etiquetado como de la vlan 2:
# tc filter add dev eth0 parent 1:0 protocol 802.1q u32 match u16 0x0002 0x0fff at -4 flowid 1:100
Ya se entenderá convenientemente este filtro cuando se discuta sobre el tipo de filtro u32.
Este tipo de filtros se basan en las entradas de las tablas de encaminamiento. Se realizan así:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 route [to realm] [from realm] [fromif link] flowid 1:10
La condición fromif es clara: el filtro se cumple cuando el paquete procede de la interfaz cuyo nombre se indica (el cual es el que aparece al
hacer un ip link show). En cambio, las otras dos exigen antes la explicación de qué es eso de realm.
Tanto ip rule como ip route permiten, al añadir una regla o una entrada, incluir el reino (realm en inglés) al que queremos que
pertenezca el origen o el destino. Por ejemplo:
Por supuesto, varias reglas y entradas pueden referirse a un mismo reino. Sabido esto, si escribimos el filtro:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 route to 10 flowid 1:10
Los paquetes que vayan dirigidos a la red 192.168.10.0/24 cumplirán con el filtro y acabarán en la clase 1:10. Si el filtro hubiera sido
route from 10, entonces acabaría en dicha clase los paquetes que procedieran de tal red.
Como estos números no son muy significativos, es posible asociarles nombres escribiéndolos en el fichero /etc/iproute2/rt_realms.
Este tipo de filtro (del que se puede obtener una descripción detallada aquí), puede resultar útil si queremos controlar tráfico según origen o
destino de los paquetes, pero es totalmente inútil si nuestro control lo queremos efectuar por tipo de tráfico.
8.4.2 Filtro fw
Consiste en filtrar atendiendo a la marca de paquete que haya podido asignarse con el cortafuegos (véase el documento dedicado a él).
Permite, pues, toda la flexibilidad en la identificación del tráfico que proporciona la manipulación con iptables y sólo tiene el
inconveniente de que estas marcas puedan entrar en conflicto con marcas que usemos para otros fines, como el encaminamiento.
docs.iescdl.es/~josem/redes.html 32/49
27-10-2019 Configuración de redes en debian
Conociendo las herramientas de netfilter es muy sencillo de usar. Por ejemplo, si nuestra intención es clasificar el tráfico de salida de
nuestro servidor web podemos hacer lo siguiente para marcarlo con un 1:
# iptables -t mangle -A OUTPUT -o eth0 -p tcp --sport 80 -j MARK --set mark 0x1
# iptables -t nat -A OUTPUT -o eth0 -p tcp --sport 80 -j CONNMARK --set mark 0x1
# iptables -t mangle -A POSTROUTING -m connmark --mark 0x1 -j CONNMARK --restore-mark
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw flowid 1:10
Este filtro se basa en inspeccionar directamente el paquete durante el proceso de control incluyendo en la misma sentencia tc filter las
reglas de filtrado. Exige en algunos casos conocer bien el formato de las cabeceras IP y las de nivel superior como TCP, UDP o ICMP.
1. una que usa selectores generales en que las comparaciones se hacen en crudo sobre las cabeceras del paquete, lo cual significa que el
filtro equivale básicamente a decir tómame los paquetes cuyos bytes tales tiene tal valor en hexadecimal. Para esto, obviamente,
debemos conocer con exactitud cuál es el formato de las cabeceras.
2. otra que usa selectores específicos, que permiten ser menos críptico al crear el filtro. Por ejemplo, tómame los paquetes cuya ip de
origen sea tal.
Por ejemplo:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match u8 6 0xff at 9 flowid 1:10
u8, u16 y u32 indican si nuestro patrón es un byte (8 bits), una palabra (16 bits) o una palabra doble (32 bits). el PATRÓN es el
valor que pretendemos encontrar en los bits en los que nos fijamos, después de haberles aplicado la MÁSCARA. Por último at nos
permite indicar el desplazamiento, esto es, a partir de qué byte desde el comienzo de la cabecera ip nos debemos fijar. Si al
desplazamiento anteponemos la expresión nexthdr el comienzo no es el comienzo del paquete (esto es de la cabecera ip), sino el
comienzo de la cabecera del protocolo de capa de transporte que, como se sabe, sigue inmediatamente a la cabecera ip. En realidad,
esta última variante para indicar el desplazamiento tras el final de la cabecera ip exige alguna cosa más, así que se pospondrá su uso
y discusión hasta que toque.
Para entender plenamente estas instrucciones podemos fijarnos en la expresión del filtro de ejemplo:
match u8 6 0xff at 9
Pretendemos comprobar si el valor del noveno (at 9) byte (u8) es 6 (en decimal, puesto que no se antecedido la cifra de 0x),
después de que haberle aplicado la máscara 0xff. Esta máscara en binario consiste en todo unos, así que dejamos el noveno byte del
paquete tiene que valor originariamente 6. Si miramos el formato de la cabecera IP (cuyo esuqema se toma de aquí:
docs.iescdl.es/~josem/redes.html 33/49
27-10-2019 Configuración de redes en debian
vemos que el noveno byte indica el protocolo de capa de transporte, cuyos identificadores se pueden consultar en
/etc/protocols. El número 6 se corresponde con el protocolo TCP, así que nuestro filtro viene a significar: tomar los paquetes
cuyo protocolo de capa de transporte sea TCP.
Si observamos el esquema, veremos que en el byte 16, comienza la dirección ip de destino y tomamos una longitud de 32 bits, que es
la longitud de estas direcciones. Si pasamos a formato CIDR el patrón y la máscara, resulta que obtenemos 192.168.255.0/24, así que
la conclusión es que pretendemos seleccionar los paquetes cuya dirección de destino sea esta red.
Para indicar varias condiciones que deben cumplirse a la vez basta con yuxtaponer las expresiones:
En este caso, seleccionamos los paquetes TCP cuya red de destino sea la 192.168.255.0/24.
Los selectores específicos, simplemente, permiten referirnos de un modo más legible a todos estos campos de la cabecera IP que
comprobamos con algo de pericia mediante los selectores generales. Al final de documento se desglosan todos los posibles, que son
muchos. Un ejemplo es este:
que equivale al penúltimo filtro que se ilustró para los selectores específicos. En principio, nos permiten comprobar valores que se
encuentren incluidos en la cabecera ip. Ahora bien, si consultamos el documento enlazado comprobaremos que hay selectores como
este:
que nos permiten indicar el puerto de destino. Esto, en realidad, es hacer trampas, porque el puerto de destino es un parámetro de los
protocolos TCP o UDP y no del protocolo IP. Tal es así, que si echamos un vistazo a los filtros definimos, veremos que el filtro que
acabamos de definir se apunta del siguiente modo:
match 00000050/0000ffff at 20
Esto es. se comprueban los cuatro primeros bytes a partir del byte 20, o sea, los custro bytes siguientes a la cabecera ip, siempre que
esta tenga efectivamente 20 bytes de longitud y no se le añadan opciones después que hacen que varíe su tamaño. Como es lo
habitual, es en este byte donde empieza la cabecera del protocolo de capa de transporte y, si consultamos un esquemos de la cabecera
TCP o UDP, veremos que los dos primeros bytes se corresponden con el puerto de origen de la conexión y los dos siguientes con el
de destino. Dada la máscara que hay en realidad estamos comparando el valor únicamente de estos dos últimos puertos, así que en
condiciones normales (y suponiendo tráfico TCP o UDP), la expresión funciona: estamos comprobando el puerto de destino. Por
supuesto, podemos yuxtaponer la expresión que asegura que el protocolo de la capa de transporte es TCP:
docs.iescdl.es/~josem/redes.html 34/49
27-10-2019 Configuración de redes en debian
Hay otro tipo de opciones que, directamente, afirman compruebar parámetros de las capas de transporte. Por ejemplo:
que aparente es equivalente a la anterior discutida. Sin embargo, si comprobamos cómo se apunta en la lista de filtros veremos lo
siguiente:
o sea, que, en vez de dar por hecho que la cabecera tiene 20 bytes usa la expresión nexthdr+0 que significa al comienzo de la
siguiente cabecera más un desplazamiento igual a 0. Dicho de otro forma, al comienzo de la cabecera del protocolo de transporte.
Sin embargo, esto escrito tal cual no funciona, porque hay que indicarle a tc cómo puede calcular la longitud de la cabecera ip. De
esto es lo que se tratará bajo un pŕoximo epígrafe. Antes, sin embargo, hay que introducir algunos conceptos más.
Cuando se crea un filtro de tipo u32, el núcleo de linux crea automáticamente una tabla hashing, que es simplemente una serie de
buckets a cada uno de los cuales se les puede asignar uns lista de filtros. Esta tabla es la tabla 800 que sólo posee un bucket. Por este
motivo si fijáramos un sólo filtro:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match u8 6 0xff at 9 flowid 1:10
La expresión fh 800: ht divisor 1 es la que indica que la tabla sólo tiene un bucket. Si observamos la siguiente regla
comprobaremos que nuestro filtro está apuntado como 800::800 (o 800:0:800), lo cual quiere decir que se encuentra en la tabla 800,
bucket 0 y su número es el 800. Si ahora añadiéramos otro filtro:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip dport 80 0xffff flowid 1:10
# tc filter show dev eth0
[...]
filter parent 1: protocol ip u32 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:10
match 00000050/0000ffff at 20
Observaríamos que este se encuentra en la misma tabla y bucket y su número es el siguiente ya que la terna de números ha de ser
única para cada regla. Esta terna se conoce como handle. Podemos especificar nosotros el número de filtro en vez de dejar que el
núcleo lo ponga por nosotros (los valores válidos van de 1 a 0xffe):
Si nos conviene, también podemos crear nuestras propias tablas. Por ejemplo, limpiemos todo y creemos la 900 con un solo bucket:
o sea, la tabla que se genera automáticamente y la que hemos creado nosotros. Ahora bien, las reglas que introduzcamos en esta tabla
no se comprobarán automáticamente a menos que enlacemos con ellas:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip protocol 6 0xff link 900:
Esto viene a significar que todo el tráfico TCP pase a ser comprobado por las reglas de la tabla 900. Creemos una regla:
# tc filter add dev eth1 parent 1:0 protocol ip prio 1 u32 ht 900: match ip sport 80 0xffff
docs.iescdl.es/~josem/redes.html 35/49
27-10-2019 Configuración de redes en debian
Obsérvese que la regla que manda todo el tráfico TCP a ser comprobado con las reglas de la tabla 900, se encuentra en la 800 y, por
tanto, se comprobará automáticamente.
La estrategia consiste en calcular la longitud de la cabecera ip al enlazar con la tabla para que se aplique en todas las reglas de la
tabla enlazada. Lo primero es crear la tabla:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 900: u32 divisor 1
Ahora hay que enlazar determinado cuál es tamaño de la cabecera. Si se observa el formato de la cabecera ip, se verá que este dato se
almacena en el segundo nibble del primer byte. Sin embargo, este dato no se almacena en bytes (que es como se expresan los
desplazamientos), sino en palabras largas, por lo que el resultado habrá que multiplicarlo por 4. Sabido todo esto el comando
apropiado es el siguiente:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 h32 offset at 0 mask 0x0f00 shift 6 plus 0 eat match i
La razón de que el comando se escriba así es que offset at 0 nos toma la primera palabra, a la cual le aplicamos la máscara
0x0f00, lo cual convierte en ceros todos los dígitos que no sean el segundo nibble del primer bytes. Como nos sobran todos los
ceros del segundo byte debemos hacer un desplazamiento de 8 hacia la derecha, pero como debemos luego multiplicar por 4,
debemos desplazar dos a la izquierda. La consecuencia es que el desplazamiento final es de 6 (shift 6). A este resultado no hay que
sumar nada (plus 0. eat es necesario para que este desplazamiento se aplique a todas las reglas de la tabla enlazada. Finalmente,
ya podemos escribir la regla en la tabla 900:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 ht 900: match tcp src 80 0xffff flowid 1:10
que hará que se compruebe bien cuál es el puerto de origen del protocolo TCP, sea cual sea el tamaño de la cabecera ip.
Este filtro es sensiblemente más potente que el anterior: permite escribir operadores lógicos (not, or and) o de comparación al introducir las
condiciones. Dispone, además, de varios módulos que nos permiten expresar nuestras condiciones de distinto modo. Repasaremos tres:
1. u32, que consiste en expresar las condiciones con selectores generales tal y como se ha indicado ya.
2. cmp, en que se pueden hacer comparaciones sobre las cabeceras de los paquetes. La ventaja sobre el anterior es que podemos indicar
sobre qué cabecera..
3. ipset, que permite hacer comprobaciones sobre los conjuntos definidos con ipset.
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 basic method "[not] modulo1(expresion) [or|and] [not] modulo2
o sea, se indica ibasic method y a continuación se hace uso de uno o varios módulos tantas veces como se quiera uniéndoles mediante
operadores lógicos. Obviamente el resultado de moduloX(expresion) será verdadero, si el paquete cumple, o falso, si no lo hace.
No necesita mucha explicación. Las expresiones que se pueden usar son aquellas capaces de escribirse con los selectores generales.
Por ejemplo, si hubiéramos querido indicar que filtrar por el puerto de origen usando selectores generales habríamos usado esta
expresión:
Pues bien, para usar el módulo u32, hay que escribir lo mismo prescindiendo de la palabra match:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 basic match "u32(u16 80 0xffff at 20)" flowid 1:10
Ya debemos saber que esto no comprueba si el protocolo es TCP o UDP, para ello había que yuxtaponer la comprobación en el filtro
u32. En este caso, habrá que usar el operador and:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 basic match "u32(u16 80 0xffff at 20) and u32(u8 6 0xf
Obviamente, no tiene mucho sentido usar este tipo de filtro, si vamos luego a usar este módulo exclusivamente.
docs.iescdl.es/~josem/redes.html 36/49
27-10-2019 Configuración de redes en debian
Este módulo es semejante al u32 en la medida en que se basa en comparar bytes de las cabeceras, pero es bastante más versátil.
Podemos obtener una ayuda de su sintaxis del siguiente modo:
Como puede observarse, las comparaciones no tienen por qué ser estrictamente iguales, si no tambié mayor o menor y se puede
especificar la capa que se quiere comprobar. El ejemplo anterior resuelto con el módulo u32 tendría esta solución:
Permite comparar el paquete con conjuntos definidos mediante ipset. Supongamos que hemos definido este conjunto:
En el que, como se ve se ha incluido el puerto 80. Para filtrar, los puertos de este conjunto habría, simplemente, que usar esta
expresión:
ipset(puertos src)
El src es debido a que nos interesa clasificar por puerto de origen (para continuar con el ejemplo). Cuando los conjuntos están
constituidos por parejas de valores (p.e. hash:net,net), entonces hay que separar src o dst con comas. Por ejemplo:
ipset(viajes src,dst)
Para hacer aún más ricos los filtros, tc permite añadir acciones (action) y control (police) al comando en que se definen. Supongamos el
siguiente filtro para el tráfico que sale por la interfaz eth0:
# tc filter add dev eth0 parent 1: protocol ip match ip dst 172.22.0.0/16 flowid 1:20
Es claro: mandaremos hacia la clase 1:20 todo el tráfico que vaya destinado a la red 172.22.0.2/16, con independencia de que este sea mucho
o poco. En cambio si añadimos una police, podremos hacer que se tomen ciertas decisiones cuando el tráfico excede un determinado umbral.
Por ejemplo:
# tc filter add dev eth0 parent 1: protocol ip match ip dst 172.22.0.0/16 action police rate 1mbit burst 128k mtu 64
El filtro es el mismo, pero se ha introducido un control que indica que si se sobrepasa la velocidad de 1 Mbit/s (aunque hay un burst de 100
kilobytes) el tráfico excedente será desechado (drop).
1. drop
Como ya se ha explicado con el ejemplo, simplemente, desecha paquetes al sobrepasar el umbral
2. pass
Deshabilita el control, es decir, actúa como si este no se hubiera escrito.
3. continue
Hace que, cuando se sobrepase el umbral, deje de ser clasificado por el filtro. Por tanto, si hay otros filtros de menor prioridad,
podrán ser clasificados por estos.
4. reclassify
Re-clasifica el tráfico que excede el umbral, de suerte que le asigna menos prioridad.
Para ilustrar lo dicho vamos a suponer que definimos una planificación para la entrada de la interfaz:
# tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid :1 action police rate 1mbit burst
docs.iescdl.es/~josem/redes.html 37/49
27-10-2019 Configuración de redes en debian
el efecto es nulo: todo el tráfico (nuestro filtro es universal) tiene un control del ratio, pero al incluir pass este simplemente se ignorará. Por
este motivo, si hacemos una prueba de velocidad con netcat (ver el próximo apartado) veremos que la limitación de 1 Mbit/s no es efectiva.
la limitación es plenamente efectiva. Asignar a este nuevo filtro la prioridad 2, no ha sido capricho, porque ahora añadiremos un nuevo filtro
con prioridad 1 sin borrar este:
# tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid :1 action police rate 1mbit burst
Es decir, el primer filtro limita el tráfico clasificado a 1 Mbit/s, pero este tráfico no es desechado, sino que con el se pasa a comprobar el
segundo filtro (continue). Este segundo filtro clasifica otro tanto de tráfico y desecha el resto. La consecuencia es que, si probamos la
velocidad de descarga, veremos que deascargas a 2 Mbit/s. No es que esto que acabamos de hacer sea muy útil, pero sirve para ilustrar cómo
funciona continue. Pero no se vayan todavía, aún hay más, que prometía Super Ratón. Si se observa la salida de la consulta de los ciclos
definidos, se verá que en cada filtro hay definida una acción que tiene un número de orden, lo cual puede hacernos llevar a pensar que para
cada filtro pueden definirse varias acciones consecutivas. Y, efectivamente, es así, y basta usar pipe. Borremos los filtros y escribamos este
otro:
# tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid :1\
action police rate 1mbit burst 128k mtu 64k\
action police rate 1mbit burst 128k mtu 64k drop
Si volvemos a consultar:
action order 2: police 0x13 rate 1Mbit burst 128Kb mtu 64Kb action drop overhead 0b
ref 1 bind 1
Obviamente, una prueba volverá a darnos una velocidad de descarga de 2 Mbit/s. En realidad, todas estas pruebas no pasan de ser meras
comprobaciones porque no hacemos nada más hayá de acabar desechando, aunque troceemos el tráfico en bandas. En este caso, hay una
primera banda de tráfico (el primer 1 Mbit/s), una segunda banda de igual anchura) y el resto del tráfico (por encima de los 2 Mbit/s, que se
desecha. Sin embargo, a cada banda se le puede asociar una acción. Por ejemplo, podemos crear dos interfaces dummy, de manera que el
primer Mbit/s lo enviemos a dummy0 y el resto a dummy1:
# tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid :1\
action mirred egress mirror dev dummy0\
action police rate 512kbit burst 128k mtu 64k pipe\
action mirred egress mirror dev dummy1
Si ahora, escuchamos tráfico con tcpdump en una y otra interfaz observaremos que ambas reciben tráfico.
mirred
Permite enviar tráfico hacia otra interfaz, bien clonándolo, bien desviándolo. Suponiendo que tengamos una una planificación para la
entrada:
# tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid :1 action mirred egress mir
Esto significa que en ambas interfaces se observarían (con tcpdump, por ejempl) todos los paquetes que entran por eth0. Como la
interfaz dummy desecha todos los paquetes que llegan a ella estos se perderían.
docs.iescdl.es/~josem/redes.html 38/49
27-10-2019 Configuración de redes en debian
En cambio, podemos optar por redirigir los paquetes hacia la salida de una interfaz ifb:
# tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid :1 action mirred egress red
En este caso, no hay clonación, sino que todos los paquetes acaban en ifb0. Esta interfaz no desecha los paquetes y, además, al
colocarlos en su salida, permite todas las planificaciones estudiadas.
xt
Permite aplicar acciones propias de iptables como si se hicieran en la tabla mangle de PREROUTING si la usamos a la entra de
paquetes, o de POSTROUTING si la usamos a la salida:
# tc filter add dev eth0 parent ffff: protocolo ip prio 1 u32 match ip src 172.22.0.0/16 flowid :1 action xt
8.4.6 Comprobación
Tan importante como saber escribir los filtros, es poder comprobar luego que, efectivamente, funcionan. Para ello, podemos generar tráfico
del tipo que clasifica el filtro en cuestión y comprobar que este actúa.
Lo primero (generar tráfico) depende mucho de la naturaleza del tráfico que hayamos querido filtrar. Si la naturaleza del tráfico era
simplemente que fuera de un tipo determinado de servicio, muy probablemente nos habremos basado en el puerto característico de este
servicio. Por ejemplo, supongamos que hemos pretendido clasificar tráfico SSH de salida, es decir, tráfico destinado al puerto 22. Si tenemos
una máquina externa con un servidor de este tipo, no hay problema y bastará con conectarse, pero si no disponemos de ella en el momento, es
fácil improvisar una conexión al puerto 22. Para ello, basta con ir a una máquina externa y poner a escuchar a netcat en el puerto 22:
Y ahora desde la máquina en que hemos implementado el control de tráfico, podemos conectarnos y empezar a descargar. Supongamos que la
ip de la máquina externa es 192.168.1.35:
El uso de pv es opcional, pero nos permite saber a qué velocidad estamos descargando y esta velocidad puede sernos útil.
Una vez que tengamos tráfico que nuestro filtro debería clasificar, podemos comprobar que efectivamente lo hace:
1. Si habíamos definido una jerarquía de clases podemos mirar las estadísticas de estas clases para comprobar que se contabilizan paquetes
en la clase por la que debería ir el tráfico:
2. Si estas clases son de la planificación HTB, en la que se definen rate y ceil, podemos probar a ver si estas se cumplen. Para ello, nos
será muy útil pv.
3. Los dos métodos anteriores exigen que hayamos creado clases sobre la planificación de una interfaz. Existe un tercer método que nos
ahora esto y que será el que se desarrolle a continuación.
Por último, podemos escuchar si el tráfico que pretendemos clasificar con el filtro, llega a ella:
Obsérvese que, usando la disciplina PRIO sin parámetros, mantenemos la planificación PFIFO_FAST predeterminado. Sin embargo, ahora sí
podemos definir el filtro que nos clona el tráfico hacia dummy0.
Por último, es importante indicar otra comprobación, aunque no relacionada con nuestro filtros, sino con la idoneidad de nuestra planificación.
En el tráfico interactivo y de inicio de conexión es muy importante la latencia, es decir, el lapso de tiempo entre que se envía un paquete y se
recibe una respuesta. Cuando se pretende calcular la latencia en general es muy fácil: es un dato que proporciona el comando ping:
docs.iescdl.es/~josem/redes.html 39/49
27-10-2019 Configuración de redes en debian
Hay información de la latencia de cada envío y una estadística final. En este caso, la latencia media es algo menor a los 27 ms.
Sin embargo, esto mide esclusivamente la latencia del protocolo icmp que puede ser totalmente diferente a la de otro tráfico dependiendo de
cómo hayamos hayamos hecho la clasificación del tráfico, que ancho le hayamos asignado a cada uno y qué planificaciones finales hayamos
dispuesto. Para medir la latencia de cualquier otro tipo de tráfico podemos valores de tcpdump; que, cuando se usa con el parámtro -ttt,
nos devuelve la diferencia que hay entre un paquete de ida y el correspondiente de respuesta. Para probarlo vamos a hacer uso del comando
ping de nuevo y a comprobar qué latencias nos devuelve este comando y qué latencias observamos con tcpdump. Obviamente deben ser las
mismas. Para ello pongamos a escuchar a tcpdump:
y a la vez, en otra terminal, problemas a mandar un par de paquetes de ping. El resultado será este:
Obsérvese que entre el primer paquete y el segundo hay una diferencia de 28.44 ms (medida que coincide con la que obtuvimos con el
comando ping y entre el tercero y el cuarto 28.22 ms.
Conocido el problema, lo primero es analizar el tráfico y clasificarlo según queramos asegurar un ancho de banda y una prioridad a cada tipo de tráfico:
1. El tráfico DNS para la resolución de nombres conviene que sea sea fluido.
2. En todas las conexiones TCP el receptor envía al emisor paquetes ACK para confirmar que va recibiendo los paquetes que éste le envía (véase
aquí). Es importante para que la bajada sea fluida que estos paquetes ACK suban con prontitud. Por tanto, para mejorar el servicio a los clientes
de la red interna, debemos reservar ancho para estos paquetes.
3. Como SSH y VPN nos sirve para administración remota, también conviene que podamos acceder a él con agilidad.
4. También conviene que reservemos un ancho de banda para que el servidor web sirva páginas.
5. Por último, podríamos decidir limitar el ancho para una determinada parte de los clientes, caracterizada porque se corresponde con una subred
determinada (p.e. 192.168.255.64/26).
Supongamos que:
Vamos a ensayar dos planificaciones: una que no tiene en cuenta la existencia de la subred penalizada; y otra que sí la tiene. Sea una o la otra
es importante hacer notar que como consecuencia de que la mayor parte del tráfico es TCP, la velocidad a la que permitamos salir paquetes de
nuestra máquina, afectará a la velocidad con que lleguen paquetes de vuelta a ella, ya que dicho protocolo acomodo la velocidad de envío a la
de recepción.
docs.iescdl.es/~josem/redes.html 40/49
27-10-2019 Configuración de redes en debian
1. Hacemos una primera distinción entre el tráfico destinado a la red local externa (clase 1:20) e internet (1:10). El tráfico hacia
internet nunca podrá superar la velocidad de subida contratada (R), así que se reserva para esta última clase ese rate y no se
define un ceil mayor. La otra clase en cambio, sí podrá llegar a ocupar todo el en ancho disponible.
2. Dentro del tráfico hacia internet se distingue:
1. Los paquetes ACK que los clientes internos envían a los servidores externos para confirmar que reciben paquetes.
Recordemos que este tráfico representa alrededor del 5% del tráfico de respuesta, o sea, que supondría unos 5 Mbit/s en
la bajada.
2. El tráfico de resolución de nombres, tanto el de consulta al exterior, como las respuestas de nuestro servidor.
3. El tráfico de control interactivo de los servidores SSH, FTP y VPN.
4. Las respuestas del servidor HTTP.
5. El resto del tráfico.
# tc class add dev eth0 parent 1:1 classid 1:100 htb rate 460kbit
# tc class add dev eth0 parent 1:1 classid 1:200 htb rate 999i540kbit ceil 1gbit
# tc class add dev eth0 parent 1:100 classid 1:10 htb rate 230kbit ceil 360kbit burst 32k prio 5
# tc class add dev eth0 parent 1:100 classid 1:20 htb rate 43kbit ceil 230kbit burst 32k prio 3
# tc class add dev eth0 parent 1:100 classid 1:30 htb rate 43kbit ceil 460kbit burst 64k prio 1
# tc class add dev eth0 parent 1:100 classid 1:40 htb rate 86kbit ceil 460kbit burst 64k prio 4
# tc class add dev eth0 parent 1:100 classid 1:50 htb rate 58kbit ceil 460kbit burst 32k prio 2
No obstante, lo anterior es interesante que discutamos el ancho de banda que reservaremos para el tráfico ACK de confirmación. El
caso que nos ocupa es el de servidor cuya conexión utilizarán bastantes clientes internos como salida a la web. Por tanto, será un
tráfico importante que afectará al rendimiento global de la conexión, por lo que el cálculo de su ancho de banda no debe hacerse muy
a la ligera, no al menos tan a la ligera como se ha definido aquí. Una estrategia es definir el ancho de subida que deseamos que
ocupen el resto de tráficos y obtener el rate para este tráfico como la diferencia entre el ancho total de subida y la suma de los rates
de los restantes tráficos. Ahora bien, este ancho de banda para los paquetes ACK de confirmación suponen el 5% del tráfico de
bajada correspondiente, por lo que haciendo una simple regla de tres, podríamos obtener este último. Si este tráfico de bajada
relacionado nos consume todo el ancho de banda disponible, no habremos hecho una buena planificación, puesto que el resto de
tráficos necesitan ancho de bajada.
La conclusión a todas estas disquisiciones es que para fijar el ancho reservado para el tráfico ACK debemos hacer dos números y
quedarnos con el más pequeño:
1. Calcular el ancho de subida disponible después de sustraer al ancho total de subida el ancho de subida que consumen los
restantes tráficos.
docs.iescdl.es/~josem/redes.html 41/49
27-10-2019 Configuración de redes en debian
2. Calcular el ancho de bajada deseable para los clientes internos como el ancho total de bajada menos los anchos de bajada que
suponen los restantes tráficos. En nuestro caso particular, podemos suponer que estos tráficos generan un tráfico de salida
(subida) igual al de entrada (bajada), salvo el del servidor HTTP que generará como tráfico de entrada un 5% del de salida.
Obtenido este ancho de bajada, podemos obtener el ancho de subida calculando el 5% (o quizás mejor un 3,5%). Además
podemos ser algo prudentes y disminuir el número obtenido por un coeficiente corrector (4/5, 5/6, etc).
Este procedimiento de cálculo del tráfico ACK está desarrollado en el módulo m1 del script de configuración, pero lo dejaremos
expresado también aquí:
Tenemos un ancho total de bajada de 5400 Kbit/s (suponiendo unas pérdidas del 10% como consecuencia del paso de la WAN a la
LAN), pero para que no se colapse el enlace vamos a aprovechar sólo el 95% (véase la limitación de la entrada, lo cual significa 5130
Kbit/s. En máxima competencia podemos suponer que el tráfico DNS ocupa unos 43 Kbit/s, el tráfico interactivo otros 43 Kbit/s, el
tráfico sin clasificar otros 58 Kbit/s y el tráfico HTTP hacia nuestro servidor web el 5% de 86 Kbit/s. Por tanto, nos queda un ancho
libre para la navegación web de 4982 Kbit/s. Este ancho de bajada supondrá un 3,5% (o un 5%) en la subida, lo que supone unos 175
Kbit/s. Finalmente podemos aplicar a este número un coeficiente reductor de 5/6 y obtendremos un rate de 145 Kbit/s. En el gráfico
y en la implementación hemos concedido al tráfico ACK 230 Kbit/s, lo cual es a todas luces excesivo. Lo mejor es tomar como
referencia el primer número que es el más pequeño y hacer pruebas en torno a él para ajustar el rate definitivo para esta clase de
tráfico. Obviamente la diferencia entre 230 y 145 (o el número deefinitivo que finalmente asignemos), no debemos desaprovecharla,
sino repartirtlo entre los otros tipos de tráfico. Esto a su vez provocaría que el ancho de banda en la bajada se modificara y
tuviéramos que rehacer el cálculo, pero como en nuestro caso particular el cambio no sería muy significativo (puesto que la bajada la
ocupa casi todo el tráfico generado por los clientes), el número no variaría mucho.
Obsérvese la definición de la planificación final para los paquetes del tráfico DNS. Se ha usado bfifo, en vez de pfifo que es
exactamente la misma planificación, excepto por el hecho de que el limit se define en bytes y no en número de paquetes, que por
cierto, en este tipo de tráfico son extraordinariamente pequeños (en torno a los 50 bytes. El modo de calcular limit ha sido en
establecer una latencia máxima. Por ejemplo, si no queremos que ningún paquete espere más de 400 ms en cola, entonces debemos
hacer la cola de 17200 bytes a lo sumo.
Ahora toca crear los filtros. Usaremos el filtro basic para casi todos ellos:
# tc filter add dev eth0 parent 1:0 protocol ip u32 match ip dst 172.22.0.0/16 flowid 1:200
# tc filter add dev eth0 parent 1:0 protocol ip basic match "u32(u8 6 0xff at 9) and \
(cmp(u8 at 13 layer transport eq 0x10) or \
cmp(u8 at 13 layer transport eq 0x2)) and \
cmp(u16 at 2 layer network lt 100)"\
flowid 1:10
# tc filter add dev eth0 parent 1:0 protocol ip basic match "u32(u8 17 0xff at 9) and \
(cmp(u16 at 2 layer transport eq 53) or\
cmp(u16 at 0 layer transport eq 53))" \
flowid 1:20
# tc filter add dev eth0 parent 1:0 protocol ip basic match "(u32(u8 6 0xff at 9) and\
cmp(u8 at 1 layer network mask 0x1c eq 0x10) an
(cmp(u16 at 0 layer transport eq 22) or
cmp(u16 at 0 layer transport eq 21))) or \
(u32(u8 17 0xff at 9) and \
cmp(u16 at 0 layer transport eq 1194))" \
flowid 1:30
# tc filter add dev eth0 parent 1:0 protocol ip basic match "u32(u8 6 0xff at 9) and\
(cmp(u16 at 0 layer transport eq 80) or\
cmp(u16 at 0 layer transport eq 443))" \
flowid 1:40
Obsérvese el primer filtro que es un poco más complicado que el resto: se buscan paquetes TCP, que tengan el marcador ACK (0x10)
(o el 0x02, que es el marcador SYN) y, además, cuya longitud sea menor de 100 bytes. Esta última condición tiene una explicación.
En el caso de un ordenador doméstico los paquetes ACK de salida serán básicamente los que envía el software cliente (un navegador,
por ejemplo) al servidor (web, en ese caso) para decirle que va recibiendo la información que le ha pedido. Estos paquetes, como son
paquetes de confirmación, son pequeños (sobre los 60 ó 70 bytes). Por otro lado, los paquetes que un servidor envía a su cliente con
la información que este le pide, también llevan únicamente el marcador ACK y miden sobre 1500 bytes; pero en el caso de un
ordenador doméstico sin servicios al exterior siempre son de entrada y no de salida. Por tanto, nos podemos ahorrar comprobar la
longitud. Ahora bien, en nuestro caso hemos dispuesto varios servidores que envían información al cliente externo con paquetes
ACK grandes. Estos no queremos de ninguna manera que vayan por la clase 1:20, porque esta está destinada exclusivamente a los
paquetes oye-esto-va-bien. Nótese que en esta clase hemos incluido también los paquetes que inician conexión.
Tembién requiere un poco de atención el filtro que identifica al tráfico ssh y ftp interactivo. Además del puerto de salida, se
comprueba que el byte ToS (aplicando una máscara 0x1c) valga 16 (o 0x10 en hexadecimal). Recuerde la explicación a ello.
En cuanto a las planificaciones finales, se ha usado PRIO sin parámetros adicionales (es decir, una PFIFO_FAST) para las clases en
las que hay tráfico de distinta prioridad, SFQ en aquellas en las que haya diversos tráficos y se quiera equilibrar el reparto del ancho
entre ellos, y PFIFO en las restantes. Obsérvese que las planificiones 2000:, 3000: y 4000: son algo discutibles. Sin embargo, en el
caso particular que se trata nuestra intención es montar un servidor DNS con lo que presumiblemente los clientes internos harán sus
consultan a él. Por tanto, todo el tráfico DNS de consulta lo originará nuestro servidor e irá dirigido al forwarder: no habrá por tanto
distintos tráficos. Las consultas externas a nuestro servidor sí se se podrán recibir desde muchos clientes distintos, pero al no tener
ser el nuestro un servidor de gran tráfico, no serán consultas muy comunes y raramente simultáneas. Sobre las otras dos
planificaciones en disputa se puede hacer idéntico razonamiento: rara vez se darán conexiones simultáneas. Tan sólo el servidor web
podría tener algo de trabajo simultáneo y poder justificar una planificación SFQ.
docs.iescdl.es/~josem/redes.html 42/49
27-10-2019 Configuración de redes en debian
8.5.1.2 Con penalización
Los principios son los mismos que en el caso anterior, pero con el fin de penalizar clientes se define una planificación PRIO
con dos bandas para la clase 1:10; de manera que a la de menor prioridad vaya el tráfico procedente de los clientes
penalizados y a el resto a la otra. Este último tráfico, posteriormente, se clasifica según los mismos criterios que en el
primer diseño. Opcionalmente se puede dejar que un poco de tráfico de la subred penalizada, vaya a la clase banda
prioritaria y compita en igualdad con el resto del tráfico.
Téngase en cuenta, si se utiliza este diseño, que a estas alturas del flujo de paquetes por el núcleo ya ha actuado iptables
(véase el flujo de paquetes a través del núcleo), de modo que si se hace un enmascaramiento (cosa bastante habitual) la ip
de origen de los paquetes ya habrá sido sustituida por la de la interfaz eth0. Así pues, en este punto es imposible distinguir
qué paquetes proceden de la subred penalizada y cuáles no. Para soslayar este problema, hay dos soluciones:
1. Marcar los paquetes con iptables antes de enmascararlos para poder identificarlos.
2. Dotar a la interfaz eth0 de dos direcciones de red: con la principal (p.e. 172.22.0.2/16 se enmascarará todo el
tráfico, excepto el proviniente de la subred limitada que se enmascarará con la ip secundaria (p.e. 172.22.255.2). Esto
de proporcionar dos direcciones distintas a una misma dirección ip es lo que se denomina ip aliasing.
Ambas soluciones nos permiten diseñar la salida, pero sólo la segunda tambíen la entrada, en la que nos encontraremos con
una situación simétrica: controlamos los paquetes antes de se traten con iptables y, por tanto, de que se deshaga el
enmascaramiento. Así, pues, no es posible comprobar si la dirección de destino pertenece a la subred penalizada, porque
docs.iescdl.es/~josem/redes.html 43/49
27-10-2019 Configuración de redes en debian
ésta será aún la de la interfaz eth0. Sin embargo, si hemos hecho ip aliasing, sí podremos basarnos en el destino para
distinguir paquetes: aquellos dirigidos a la ip adicional van dirigidos a la subred.
Así pues, haremos ip aliasing y enmascararemos con la segunda dirección la subred de marras, como paso previo:
# tc class add dev eth0 parent 1:1 classid 1:100 htb rate 460kbit
# tc class add dev eth0 parent 1:1 classid 1:200 htb rate 999i540kbit ceil 1gbit
# tc qdisc add dev eth0 parent 1:100 handle 100: prio bands 2 priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# tc qdisc add dev eth0 parent 100:1 handle 1001: htb default 50
# tc class add dev eth0 parent 1001: classid 1001:100 htb rate 512kbit
# tc class add dev eth0 parent 1001:100 classid 1001:10 htb rate 230kbit ceil 400kbit burst 32k prio
# tc class add dev eth0 parent 1001:100 classid 1001:20 htb rate 43kbit ceil 230kbit burst 32k prio
# tc class add dev eth0 parent 1001:100 classid 1001:30 htb rate 43kbit ceil 460kbit burst 64k prio
# tc class add dev eth0 parent 1001:100 classid 1001:40 htb rate 86kbit ceil 460kbit burst 64k prio
# tc class add dev eth0 parent 1001:100 classid 1001:50 htb rate 58kbit ceil 460kbit burst 32k prio
Quedan, por último, los filtros. En realidad, son los mismos que para el caso anterior (salvando la diferencia en los handle,
excepto el nuevo que aparece para separar el tráfico en la planificiación 100:. Callemos este último, por ahora, y
desglosemos el resto:
# tc filter add dev eth0 parent 1:0 protocol ip u32 match ip dst 172.22.0.0/16 flowid 1:20
# tc filter add dev eth0 parent 1001: protocol ip basic match "u32(u8 6 0xff at 9) and \
(cmp(u8 at 13 layer transport eq 0x10)
cmp(u8 at 13 layer transport eq 0x2))
cmp(u16 at 2 layer network lt 100)"\
flowid 1001:10
# tc filter add dev eth0 parent 1001: protocol ip basic match "u32(u8 17 0xff at 9) and \
(cmp(u16 at 2 layer transport eq 53) o
cmp(u16 at 0 layer transport eq 53))"
flowid 1001:20
# tc filter add dev eth0 parent 1001: protocol ip basic match "(u32(u8 6 0xff at 9) and \
cmp(u8 at 1 layer network mask 0x1c e
(cmp(u16 at 0 layer transport eq 22) o
cmp(u16 at 0 layer transport eq 21)))
(u32(u8 17 0xff at 9) and \
cmp(u16 at 0 layer transport eq 1194)
flowid 1001:30
# tc filter add dev eth0 parent 1001: protocol ip basic match "u32(u8 6 0xff at 9) and\
(cmp(u16 at 0 layer transport eq 80) o
cmp(u16 at 0 layer transport eq 443))
flowid 1001:40
Obviamente, son los mismos exceptuando el hecho que los filtros que dividen el tráfico a internet se aplican sobre la
planificación 1001:. Sin embargo, queda el filtro que segrega el tráfico de la subred penalizada hacia la banda 100:2. Puede
ser este:
# tc filter add dev eth0 parent 100: protocol ip prio 1 u32 match ip src 172.22.255.2/32 flowid 100:1
action police rate 32kbit burst 64k mtu 64k continue
# tc filter add dev eth0 parent 100: protocol ip prio 2 u32 match ip src 172.22.255.2/32 flowid 100:2
Con los filtros se pretende que, en situación de competencia, la red penalizada nunca ocupe más de un 10% del ancho de
bajada. Por este motivo (y asumiendo que prácticamente todo el tráfico de subida de estos clientes es de confirmación) se
permite que 32 Kbit/s se traten como tráfico no penalizado. El número se obtiene de aplicar el 5% al 10% del tráfico de
bajada y redondear hacia arriba un poco. Si no se quiere tener estos miramientos, se puede eliminar el primer filtro.
El control de tráfico más eficiente que puede hacerse para gestionar la entrada, es haber configurado eficientemente la salida; y este es el que
se sugiere. Existen, no obstante, mecanismos para realizar planificaciones a la entrada semejantes a la de la salida. Se indicará cómo hacerlos
más adelante.
docs.iescdl.es/~josem/redes.html 44/49
27-10-2019 Configuración de redes en debian
Lo recomendable a la entrada es, simplemente, limitar la bajada para impedir que se sature el enlace y se disparen las latencias. Para
ello podemos hacer lo siguiente:
aunque habrá que ajustar rate (siempre deberá estar por debajo 5400) y hacer pruebas para comprobar cómo se comporta la latencia.
Por ejemplo, podemos poner a descargar un fichero que ocupe todo el ancho de banda y efectuar algunas pruebas de ping.
DESACONSEJADO
Hay varias soluciones para lograr crear planificaciones del tráfico de entrada:
a. Si limitamos el tráfico a la salida en la interfaz interna eth1 que conecta con la subred penalizada, soslayamos el problema
de vérnoslas con la planificación de entrada y, aunque hayamos hecho enmascaramiento, los paquetes ya dispondrán de su ip
de destino definitiva, por tanto será muy fácil establecer la limitación. Sin embargo, es una solución muy deficiente si eth1
no es la única interfaz interna, puesto que por ella no pasará todo el tráfico que entró por eth0. Si, además, disponemos
varios servicios en nuestra máquina, ni siquiera sería solución recomendable si fuera la única interfaz interna. En estas
condiciones, lo único que podríamos hacer es limitar el ancho de banda para la subred penalizada (con un filtro, por ejemplo),
pero esto precisamente también lo podríamos hacer con la planificación a la entrada de eth0. Así pues, desechamos esta
estrategia
b. Si hacemos el ip aliasing como ya propusimos, podemos usar una interfaz virtual ifb para redirigir a su salida el tráfico que
entra por eth0. De este modo, hemos convertido la entrada en una salida y podremos aplicar planificaciones de salida.
c. Usar una interfaz virtual imq a cuya salida se redirige el tráfico, cuando es tratado por netfilter. Tiene la ventaja sobre la
interfaz cirtual ifb que para entonces ya se ha deshecho el enmascaramiento (no se necesita, por tanto, ip aliasing) y que se
puede identificar y marcar el tráfico con iptables. En cambio, exige parchear y recompilar el kernel, así que desecharemos
esta vía.
Desarrollemos la segunda de las soluciones. Hecho el ip aliasing, debemos crear una interfaz virtual ifb lo cual exige cargar el
módulo:
A continuación mandamos todo el tráfico entrante procedente de internet (o sea, el que no provenga de 172.22.0.0/16) hacia
esta interfaz:
Hecho esto, volvemos a encontrarnos ante un caso de control sobre el tráfico de salida y podemos aplicar todo lo aprendido para
gestionar el tráfico de internet. Podemos, por ejemplo, definir una planificación PRIO igual a la 100: que ideamos anteriormente en
la que el tráfico menos prioritario es el destino a la subred penalizada:
docs.iescdl.es/~josem/redes.html 45/49
27-10-2019 Configuración de redes en debian
Excepcionalmente hemos dado el handle 100: a la planificación root y no el 1: con el propósito de que los identificadores de
planificaciones y clases sean lo más parecidos al diseño de la planificación de salida. Para no ser muy prolijos, si en vez de crear
una planificación HTB para la banda 100:1, creamos una planificación PRIO que emula una PRIO_FAST, los comandos quedan así:
# tc qdisc add dev ifb0 root handle 100: prio bands 2 priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Y el filtro para desviar el tráfico penalizado hacia 1:2, es igual al ya definido en la entrada, salvo por el hecho de que ahora la ip es
la de destino y no la de origen:
# tc filter add dev ifb0 parent 100: protocol ip prio 1 u32 match ip dst 172.22.255.2/32 flowid 100:1 \
action police rate 51kbit burst 64k mtu 64k continue
# tc filter add dev ifb0 parent 100: protocol ip prio 2 u32 match ip dst 172.22.255.2/32 flowid 100:2
Con esto, podría valer. Ahora bien, desarrollar el diseño dibujado en el gráfico exige unos cuantos comandos más.
docs.iescdl.es/~josem/redes.html 46/49
27-10-2019 Configuración de redes en debian
#!/bin/bash
LOCKFILE="/var/lock/qos.IFACE.lock"
MODULESDIR="/usr/lib/qos-modules"
LOSS=10 # Pérdidas de un 10% por la encapsulación WAN.
DESC=95 # Disminuye la bajada para no saturar el enlace.
LIMITED_BANDWIDTH=${LIMITED_BANDWIDTH:-0}
function parameter() {
[ -z "$2" -o "${2:0:1}" == "-" ] && \
echo "ERROR $1 necesita un argumento " >&2 && exit 1
Para ver cómo funciona no hay más que pedir la prolija ayuda:
# qos.sh -h
En principio, los controles de salida y entrada que implementa son muy simples: los que se han discutido, pero sin distinguir en el tráfico no
penalizado, de manera que en vez de generarse las clases 1:10, 1:20, etc. hay una planificación PRIO idéntica a la PFIFO_FAST.
Es posible escribir módulos que implementen cualquier otra política más complicada. El módulo m1 define exactamente las discutidas en el
tema y puede servir de modelo para escribir otras:
#!/bin/bash
#
# m1: Módulo para script qos.sh
#
# Define para el script las funciones "uplink_police" y "downlink_police"
# en sustitución de "default_uplink_police" y "default_downlink_police".
# Si alguna no se define, se usará su correspondiente predeterminada.
#
# Si se quieren definir variables o funciones auxiliares, los nombres
# deben empezar por "m_", para que no haya conflicto con las funciones
# definidas en el programa principal.
#
# Variables definidas:
#
# OUTPUT_IFACE: Interfaz de salida (para uplink_police).
# IFB: Interfaz ifb virtual (para downlink_police). Sin una, siempre
# se ejecutara default_downlink_police.
# ADDRESS: IP principal en formato CIDR.
# ADDRESS2: Ip secundaria en formato CIDR.
# NET: Array con la red a la que pertenece la interfaz de salida.
# El primer elemento es la ip y el segundo la máscara, ambos en
# formato hexadecimal. P.e. ( c0a80100 ffffff00 ) es la
# red 192.168.1.0/24.
# DOWNLINK: Ancho de banda de bajada.
# UPLINK: Ancho de banda de subida.
# LIMITED_BANDWIDTH: Porcentaje del ancho de banda que como máximo ocuparan
# la subred penalizada cuando entre en competencia con otros
# clientes
El módulo habrá que dejarlo en el directorio que indique la variable MODULESDIR del script. Como ejemplo, para implementar los control de
salida y entrada expuestos en el tema, basta con lo siguiente:
El script no se encarga en absoluto de cargar el módulo ifb para que haya disponibles interfaces ifb: esto deberá hacerse a mano o
integrarse en algún script de arranque.
Todas:
docs.iescdl.es/~josem/redes.html 47/49
27-10-2019 Configuración de redes en debian
$ ip link show
Todas:
$ ip addr show
Activación:
Desactivación:
$ ip neigh show
Adición:
Eliminación:
Consulta de rutas
Consulta de rutas:
# ip route show
Adición de rutas
docs.iescdl.es/~josem/redes.html 48/49
27-10-2019 Configuración de redes en debian
Eliminación de la entrada para una red particular:
Creación de puentes
Eliminación de la subinterfaz:
Bibliografía
[1] Chapter 5. Network setup[www.debian.org]: Capítulo 5 del manual oficial de debian, que trata sobre la configuración de red.
[2] iproute2 cheat sheet[baturin.org]: Recopilación de comandos que pueden hacerse con iproute2.
[3] Comparison of BRTCL and BRIDGE commands[sgros-students.blogspot.com.es].
[4] Virtual switching technologies and Linux bridge[events.linuxfoundation.org]: Interesante exposición (transparencias) sobre switching con linux.
[5] Linux Advanced Routing & Traffic Control Howto[www.lartc.org]: El manual de referencia sobre encaminamiento avanzado y control de tráfico con linux.
GULIC produjo una traducción de este documento del que se ha procurado conservar una copia.
[6] Dual Uplink BCP[www.cyber.com.au]
[7] Traffic Control, Shaping and QoS[wiki.linuxwall.info]: ilustrativo artículo en inglés sobre control de tráfico y calidad de servicio.
[8] Traffic Control HOWTO[linux-ip.net]: Manual sobre control de tráfico con linux.
[9] Notes on the Linux Traffic Control Engine[linux-tc-notes.sourceforge.net]: Estas notas contienen información muy detallada sobre algunos aspectos de tc (p.e.
sobre el filtro u32).
[10] Traffic Control[wiki.wlug.org.nz]: Algunos apuntes muy en borrador sobre tc. Está desglosada la forma de pedir ayuda a tc sobre las distintas
planificaciones y filtros.
[11] Understanding TCP Sequence abd Acknowledgment Numbers[packetlife.net]: Interesante documento que destripa una comunicación TCP.
docs.iescdl.es/~josem/redes.html 49/49