Posted by : xor domingo, 3 de mayo de 2009

Voy a explicar como configurar la priorización de paquetes (QoS: Quality Of Service) usando una instalación OpenBSD (Packet Filter ALTQ). Es importante que tengas algunas nociones previas de Packet Filter ya que voy a ir directamente al tema y no voy a detenerme en conceptos básicos. Tené la documentación que ofrece OpenBSD a mano: Packet Filter



Para los que no saben de que se trata esto del QoS, como su nombre en Inglés lo indica, es mantener "la calidad de los servicios" de nuestra red.

Y para que quiero mantener la "calidad de los servicios"? Un caso muy clásico es cuando hacemos un donwload (de alguna página o algún torrent) de tamaño grande que tarda varios minutos en terminar y mientras tanto nuestra conexión se torna inusable para hacer otras cosas, como por ejemplo navegar. Con la priorización de paquetes podemos solucionar este problema, siempre y cuando realicemos una correcta configuración. Otro caso de uso muy útil es si contamos con telefonía IP (VOIP) que para mantener la calidad de las comunicaciones es imprescindible contar con un buen flujo de datos que no se interrumpa, con la priorización de paquetes nos aseguramos que esos datos no sean interrumpidos por otros datos de otras conexiones dándoles prioridad.


Por que OpenBSD? OpenBSD es una plataforma de libre uso muy sólida y estable que su principal enfoque es la seguridad, razón por la cual posee un firewall similar a iptables de GNU/Linux llamado Packet Filter que es muy potente y sencillo de configurar, que además de proveer funciones de filtrado, ofrece entre otras cosas, priorización de paquetes que lo denomina ALTQ (Alternate Queueing). OpenBSD puede instalarse en un equipo i386 o superior, con lo cual podemos re-utilizar cualquier equipo que ya no usemos y armar el más potente firewall con priorización de paquetes. En mi experiencia personal, la primera vez que armé un firewall con priorización de paquetes sobre OpenBSD fue hace algunos años en un i486DX2 y lo tuve funcionando alrededor de 2 años hasta que reemplacé el equipo por uno más actual y jamás sufrió un solo cuelgue.

Puedo usar GNU/Linux? No hay Packet Filter para GNU/Linux. GNU/Linux con su iptables también pueden configurarse para hacer priorización de paquetes, pero es algo más rebuscado, con Packet Filter es mucho más simple.

Puedo usar FreeBSD, NetBSD o algún otro producto de la familia BSDs? Sí, se puede tanto FreeBSD como NetBSD cuentan con un port de Packet Filter (que es un proyecto que nació de OpenBSD) pero por defecto no cuentan con soporte ALTQ, lo que nos obliga a recompilar el kernel para poder utilizarlo.

Usando ALTQ podemos encolar (Queueing) paquetes utilizando 2 modelos diferentes:

  1. CBQ (Class Based Queueing): Este modelo permite segmentar el ancho de banda en varios fragmentos que podemos asignar a los diferentes servicios. Es muy interesante pero no lo vamos a usar acá.
  2. PRIQ (Priority Queueing): Este modelo permite definir a que servicios se les debe dar "prioridad de paso" y es el que vamos a utilizar más abajo.

Lo primero que debemos hacer es decidir es a que vamos a darle prioridad para posteriormente armar las reglas que correspondan.

Supongamos que tenemos una red doméstica con un ancho de banda de 2Mbits en donde principalmente: Naveguemos, usemos mensajería instantánea, hagamos llamadas de voz sobre IP, realicemos sesiones de SSH y bajemos algunos torrents, entre otras cosas.

Nuestro objetivo es: Bajar torrents todo el día (es ejemplificativo, nadie de ustedes baja torrents todo el día no? :) y que nuestras sesiones SSH, mensajería instantánea y navegación por la web no se tornen lentas ni sean interrumpidas (timeouts). A todo esto también hacemos muchas llamadas vía VOIP que no deseamos que se entre-corten ni tengan ruidos de fondo, sino que sigan manteniendo la misma claridad como si fuesen realizadas desde un teléfono convencional, incluso si estoy bajando mi serie favorita. Suena interesante no?

Vamos a empezar con un pf.conf básico como este:


# Definimos nuestras interfaces de red
ext_if = "rl0" # Interface de red externa
int_if = "rl1" # Interface de red interna.

# Definimos estas dos variables que vamos a utilizar más abajo
bw_upload = "160Kb" # Ancho de banda que usaremos para el upload (160Kbits = 20KB)
tcp_options = "flags S/SA keep state" # Guardar el estado de la conexión (TCP)

# Definimos los puertos que vamos a manipular
port_voip = "{ 5060 5061 }" # VOIP TCP (Por ejemplo Asterisk)
port_voip_udp = "{ 5060 4569 5061 5036 10000:20000 2727 }" # VOIP UDP
port_ssh = "{ 22 }" # SSH
port_http = "{ 80 443 }" # HTTP, HTTPS
port_im = "{ 25 110 993 995 6667 7000 1863 5190 5222 }" # Mail, MSN, IRC, etc
port_p2p = "{ 6881:6999 }" # P2P (Cambiar por los puertos que correspondan en tu caso)

# Bloquear utilizando drop en lugar de deny
set block-policy drop

# Normalizado de paquetes
scrub in on $ext_if all fragment reassemble
scrub in on $int_if all no-df

A continuación debemos armar las colas de prioridad sobre la interface externa. Es posible especificar colas en otras interfaces, pero normalmente contamos con 100MB en la interface interna (algunos con más suerte incluso con 1000MB), con lo cual nos sobra ancho de banda y no necesitamos priorizar paquetes aquí ya que desde Internet jamás vamos a recibir tanto tráfico simultáneo (a no ser que cuentes con una conexión con ese increible ancho de banda). A la cola debemos decirle cuanto ancho de banda vamos a asignarle, nosotros utilizamos $bw_upload que son 160Kbits o 20KB, lo normal (cuando nuestro ISP no nos roba demaciado) es que contemos con 25KB de upload para una conexión de 2Mbits de download (256MB), los 5K sobrantes los dejamos libres para el correcto tráfico de los paquetes ACK.

altq on $ext_if priq bandwidth $bw_upload queue \
{ p2p_queue, std_queue, http_queue, im_queue, ssh_bulk_queue, ssh_login_queue, dns_queue, voip_queue, ack_queue }

A cada cola hay que asignarle un número de prioridad que tendrán los paquetes que pertenezcan a la misma. Este es el punto más importante, si están mal las prioridades, no vamos a lograr nuestro objetivo de priorizar lo mas importante. Cuanto más alto el número más prioridad van a tener los paquetes correspondientes. OpenBSD permite hasta 16 colas (de 0 a 15).

queue p2p_queue priority 2
queue std_queue priority 4 priq(default)
queue http_queue priority 8
queue im_queue priority 9
queue ssh_bulk_queue priority 10
queue ssh_login_queue priority 11 priq(red)
queue dns_queue priority 12
queue voip_queue priority 14
queue ack_queue priority 15

En primer lugar hemos definido que los programas P2P tengan el mínimo de prioridad, incluso por debajo del umbral declarado por defecto. Es decir que cualquier conexión declarada o no en nuestro pf.conf va a tener más prioridad que las descargas P2P.

La segunda cola std_out es la cola por defecto, todas las conexiones que no hayan sido explicítamente mencionadas en nuestro pf.conf serán asignadas a esa cola (por ejemplo FTP, Juegos online, etc)

La navegación web tendrá más prioridad que los P2P y la cola por defecto.

La mensajería instantánea, mail e IRC tendrán más prioridad incluso que la navegación web.

Las sesiones SSH irán por encima de las ya mensionadas. Nótese que hay 2 colas para SSH, una para transferencia (scp) y otra para login con más prioridad aún. El scheduler "RED" (Random Early Detection) es para evitar congestiones haciendo que la cola nunca se llene.

El DNS siempre debe ser un servicio con la prioridad más alta para evitar demoras al resolver dominios.

Las llamadas de voz sobre IP tienen el nivel más alto, solamente por debajo de los paquetes ACK, ya que no queremos que nada interfiera cuando tenemos una llamada en curso.

Y por último los paquetes ACK deben ser SIEMPRE la mayor prioridad.

Seguimos con las reglas de NAT:

# Acceso a internet a cualquier maquina de nuestra LAN
nat on $ext_if from $int_if:network to any -> $ext_if

Las entradas de forwarding de puertos, en este caso vamos a forwardear los puertos del torrent:

# Forwardeo de puertos
rdr on $ext_if proto tcp from any to any port $port_voip -> 192.168.0.2 # Cambiar IP destino según corresponda
rdr on $ext_if proto udp from any to any port $port_voip_udp -> 192.168.0.2 # Cambiar IP destino según corresponda
rdr on $ext_if proto { tcp udp } from any to any port $port_p2p -> 192.168.0.2 # Cambiar IP destino según corresponda

Siguen las entradas de bloqueo/acceso. No vamos a bloquear nada, si bien pudieramos, ya que no es el punto establecer un firewall, sino priorizar paquetes. Que cada uno establezca las reglas de bloqueo a su preferencia.

Los paquetes a las colas se asignan utilizando la sentencia "pass" (sí, la misma) salvo que se agrega la cláusula "queue". Vamos a ir asignando una por una.

Primero las conexiones salientes:

# Todo es standard excepto los ACK que los enviamos a ack_queue
pass out on $ext_if proto tcp from any to any \
$tcp_options queue(std_queue, ack_queue)

# Telefonia IP TCP a voip_queue
pass out on $ext_if proto tcp from any to any port $port_voip \
$tcp_options queue voip_queue

# Telefonia IP UDP a voip_queue
pass out on $ext_if proto udp from any to any port $port_voip_udp \
keep state queue voip_queue

# Consultas DNS a dns_queue
pass out on $ext_if proto { tcp udp } from any to any port domain \
keep state queue dns_queue

# Transferencias SSH a ssh_bulk_queue y login a ssh_login_queue
pass out on $ext_if proto tcp from any to any port $port_ssh \
$tcp_options queue(ssh_bulk_queue, ssh_login_queue)

# Mail, Mensajería instantanea, IRC, etc a im_queue
pass out on $ext_if proto { tcp udp } from any to any port $port_im \
$tcp_options queue im_queue

# HTTP, HTTPS a http_queue
pass out on $ext_if inet proto tcp from any to any port $port_http \
$tcp_options queue http_queue

Luego las conexiones entrantes:

# Telefonia IP TCP entrante a voip_queue
pass in on $ext_if proto tcp from any to any port $port_voip \
$tcp_options queue voip_queue

# Telefonia IP UDP a voip_queue
pass in on $ext_if proto udp from any to any port $port_voip_udp \
keep state queue voip_queue

# Conexiones P2P entrantes a p2p_queue (Estas son las que menoes prioridad tienen de todas)
pass in on $ext_if proto { udp tcp } from any to any port $port_p2p \
$tcp_options queue p2p_queue

Eso es todo, ya contamos con nuestras reglas listas, ahora debemos cargarlas:

# pfctl -f /etc/pf.conf

Para monitorear su correcto funcionanamiento podemos utilizar la herramienta pftop que se encuentra disponible para instalar en el repositorio de OpenBSD. (Tecla 8 para acceder al panel de colas)

QUEUE BW SCH PRIO PKTS BYTES DROP_P DROP_B QLEN BORROW SUSPEN P/S B/S
p2p_out priq 2 827936 267887K 317844 43731134 0 0 0
std_out priq 4 8215688 8569649K 153130 97944K 0 9 13475
http_out priq 8 2809938 634870K 10757 2688711 0 0 0
im_out priq 9 344323 73140938 90 98819 0 0 0
ssh_bulk_out priq 10 33036 45973216 0 0 0 0 0
ssh_login_out priq 11 6873 1253422 1 146 0 0 0
dns_out priq 12 102845 8951957 0 0 0 0 0
voip_out priq 14 788399 189578K 123 67892 0 0 0
ack_out priq 15 7367822 453643K 517 34254 0 0.4 23

Aquí dejo el archivo de configuración que acabamos de armar completo:

# -------------------------------------------------------------------------
# Packet Filter ALTQ rules by Hernando Furlan - Written under OpenBSD 4.5
# $Id: pf.conf,v 1.0 2009/05/02 16:14:11 nando Exp $
#
# Hernando Furlan
# http://www.nandox.com - http://about.nandox.com - http://www.ifork.com.ar
# -------------------------------------------------------------------------

# Definimos nuestras interfaces de red
ext_if = "rl0" # Interface de red externa
int_if = "rl1" # Interface de red interna.

# Definimos estas dos variables que vamos a utilizar más abajo
bw_upload = "160Kb" # Ancho de banda que usaremos para el upload (160Kbits = 20KB)
tcp_options = "flags S/SA keep state" # Guardar el estado de la conexión (TCP)

# Definimos los puertos que vamos a manipular
port_voip = "{ 5060 5061 }" # VOIP TCP (Por ejemplo Asterisk)
port_voip_udp = "{ 5060 4569 5061 5036 10000:20000 2727 }" # VOIP UDP
port_ssh = "{ 22 }" # SSH
port_http = "{ 80 443 }" # HTTP, HTTPS
port_im = "{ 25 110 993 995 6667 7000 1863 5190 5222 }" # Mail, MSN, IRC, etc
port_p2p = "{ 6881:6999 }" # P2P (Cambiar por los puertos que correspondan en tu caso)

# Bloquear utilizando drop en lugar de deny
set block-policy drop

# Normalizado de paquetes
scrub in on $ext_if all fragment reassemble
scrub in on $int_if all no-df

altq on $ext_if priq bandwidth $bw_upload queue \
{ p2p_queue, std_queue, http_queue, im_queue, ssh_bulk_queue, ssh_login_queue, dns_queue, voip_queue, ack_queue }

queue p2p_queue priority 2
queue std_queue priority 4 priq(default)
queue http_queue priority 8
queue im_queue priority 9
queue ssh_bulk_queue priority 10
queue ssh_login_queue priority 11 priq(red)
queue dns_queue priority 12
queue voip_queue priority 14
queue ack_queue priority 15

# Acceso a internet a cualquier maquina de nuestra LAN
nat on $ext_if from $int_if:network to any -> $ext_if

# Forwardeo de puertos
rdr on $ext_if proto tcp from any to any port $port_voip -> 192.168.0.2 # Cambiar IP destino según corresponda
rdr on $ext_if proto udp from any to any port $port_voip_udp -> 192.168.0.2 # Cambiar IP destino según corresponda
rdr on $ext_if proto { tcp udp } from any to any port $port_p2p -> 192.168.0.2 # Cambiar IP destino según corresponda

# Todo es standard excepto los ACK que los enviamos a ack_queue
pass out on $ext_if proto tcp from any to any \
$tcp_options queue(std_queue, ack_queue)

# Telefonia IP TCP a voip_queue
pass out on $ext_if proto tcp from any to any port $port_voip \
$tcp_options queue voip_queue

# Telefonia IP UDP a voip_queue
pass out on $ext_if proto udp from any to any port $port_voip_udp \
keep state queue voip_queue

# Consultas DNS a dns_queue
pass out on $ext_if proto { tcp udp } from any to any port domain \
keep state queue dns_queue

# Transferencias SSH a ssh_bulk_queue y login a ssh_login_queue
pass out on $ext_if proto tcp from any to any port $port_ssh \
$tcp_options queue(ssh_bulk_queue, ssh_login_queue)

# Mail, Mensajería instantanea, IRC, etc a im_queue
pass out on $ext_if proto { tcp udp } from any to any port $port_im \
$tcp_options queue im_queue

# HTTP, HTTPS a http_queue
pass out on $ext_if inet proto tcp from any to any port $port_http \
$tcp_options queue http_queue

# Telefonia IP TCP entrante a voip_queue
pass in on $ext_if proto tcp from any to any port $port_voip \
$tcp_options queue voip_queue

# Telefonia IP UDP a voip_queue
pass in on $ext_if proto udp from any to any port $port_voip_udp \
keep state queue voip_queue

# Conexiones P2P entrantes a p2p_queue (Estas son las que menoes prioridad tienen de todas)
pass in on $ext_if proto { udp tcp } from any to any port $port_p2p \
$tcp_options queue p2p_queue

{ 1 comentarios... read them below or add one }

  1. Hola,

    Super interesante este post... despejaste demasiadas dudas que tenia en cuanto a realizar estas configuraciones en PF.

    Muchas Gracias.

    Saludos...

    ResponderEliminar

Popular Post

Labels

Seguidores

- Copyright © NandOX Chat - Blog -Metrominimalist- Powered by Blogger - Designed by Johanes Djogan - Modified by NandOX