DHCP (Protocolo Dinámico de Configuración de Equipo) es un protocolo de red que permite a la máquina obtener una configuración de red sin intervención del usuario. A esta forma automática de obtener la configuración se la suele denominar como configuración dinámica, frente a la configuración manual, que exige que se introduzcan manualmente los parámetros de red (fundamentalmente ip, máscara, puerta de enlace y servidores dns).
Tiene su origen en el protocolo BOOTP (Protocolo de arranque) que se usaba antiguamente en las máquinas UNIX sin disco para que obtuviesen una dirección IP aun antes de arrancar cualquier sistema operativo. De hecho, el modo que tenían estas máquinas sin disco de obtener el sistema operativo, era gracias a que después de obtenida ip, descargaba la imagen gracias a un servidor TFTP (Protocolo Trivial de Tranferencia de Ficheros). Este esquema de arranque se sigue utilizando hoy día en las terminales tontas, aunque se ha sustituido el protocolo bootp por el dhcp.
La comunicación se realiza entre el puerto 67/UDP del servidor y el puerto 68/UDP del cliente, y puede deberse a varias causas, las más comunes de las cuales son:
El protocolo se inicia cuando un cliente que requiere una configuración de red pide una dirección ip mediante el envío de un paquete DHCPDISCOVER. Al no tener configurada aún el cliente su interfaz, usa como dirección de origen la 0.0.0.0 y como dirección de destino la de broadcast universal 255.255.255.255.
Este paquete llegará a todas las máquinas de la red y, si alguna es un servidor dhcp responderá enviando un paquete DHCPOFFER en el que le propone al cliente una dirección ip libre. Este paquete usa como dirección de origen la ip del servidor y como dirección de destino la ip ofrecida, aunque antiguamente se usaba la dirección de broadcast universal.
Si el cliente acepta está ip, envía entonces una petición formal al servidor dhcp mediante un paquete DHCPREQUEST en el que incluye una lista de parámetros de red que le gustaría que el servidor le proporcionara, Esta lista de parámetros es una lista de números enteros, ya que cada parámetro de red está representado por un número. Estos parámetros se refieren a la máscara de red, la dirección ip de la puerta de enlace, las direcciones ip de los servidores DNS, etc. Este paquete tiene las mismas direcciones de origen y destino que el DHCPDISCOVER, puesto que el cliente sigue sin tener una configuración de red.
Recibido el paquete de petición por el servidor, este responde con un paquete DHCPACK donde se incluyen todos los parámetros que requiere el cliente y además se le informa de cuál es el periodo de vigencia de la concesión. Las direcciones de origen y destino son las mismas que para el paquete DHCPOFFER. Cuando este paquete es recibido por el cliente, este es capaz de configurar por fin sus parámetros y podrá participar normalmente de la comunicación en red.
Cuando la concesión de una ip está próxima a expirar, el cliente puede enviar un paquete DHCPREQUEST en que pide al servidor que le prorrogue la concesión de la ip. Este paquete tiene las mismas direcciones de origen y destino que el DHCPREQUEST que se explicó bajo el epígrafe anterior.
Si el servidor accede, entonces devolverá un paquete DHCPACK con la configuración de red y el cliente disfrutará de la ip durante otro periodo de tiempo.
Si el cliente desea deshacer la concesión antes de que expire el plazo (por ejemplo, porque va a apagarse y no desea seguir ocupando la dirección), puede entonces enviar al servidor un paquete DHCPRELEASE que utiliza como dirección de origen la ip del cliente y como dirección de destino la del servidor. Al recibir este paquete el servidor dará por finalizada la concesión y liberará la ip para que pueda ser entregada de nuevo.
Si el cliente no envía este aviso, el servidor esperará a que el plazo expire para liberar la ip. Es más, si posteriormente el mismo cliente, por cualquier razón, inicia la petición de una nueva ip con un paquete DHCPREQUEST (ver apartado siguiente) en que sugiere una ip distinta y esta está libre, el servidor se la concederá sin liberar la ip anterior. cuyo plazo puede aún no haberse cumplido. Para que esta circunstancia no se produzca y un mismo cliente siempre consuma sólo una ip es necesario fijar en el servidor del ISC la directiva:
one-lease-per-client on;
Los tres casos anteriores son los más habituales. Sin embargo, pueden darse otros. Uno muy común se produce cuando el cliente, sin la concesión previa de una determinada ip, envía directamente al servidor un paquete DHCPREQUEST en que sugiere que se le conceda tal ip.
Si la ip está libre, el servidor aceptará la sugerencia del cliente y enviará un paquete DHCPACK. Sin embargo, si esa ip no está libre, no podrá concederla y le enviará un paquete DHCPNACK con el que le notifica la denegación. Este paquete tiene como ip de origen el servidor y como ip de destino la de broadcast universal 255.255.255.255.
Ante la denegación, el cliente comenzará el protocolo de pedir una nueva ip mediante un paquete DHCPDISCOVER.
Hay aún una cuestión en el aire: ¿cómo es capaz de distinguir e identificar el servidor a sus clientes? La respuesta más evidente es responder que a través de la MAC de su interfaz de red. Sin embargo, esto no es del todo cierto. El protocolo establece que la forma de identificar el servidor a los clientes es mediante un parámetro UID (en dhclient este parámetro se llama dhcp-client-identifier) que debe enviar el cliente al servidor al comunicarse con él. Sólo en caso de que el cliente no envíe ningún UID, el servidor se valdrá de la dirección MAC. Es importante tener esto presente, puesto que hay clientes dhcp que de modo predeterminado no mandan UID (p.e. los que incorporan las tarjetas para un arranque por red) y otros que forzosamente lo hacen como el de windows. Este hecho provocr que, si se cumple con el RFC correspondiente, un mismo ordenador (con una única tarjeta de red) pueda acaparar dos ips, si arranca con un sistema, no informa al servidor de que libere la ip, y arranca luego con el otro. Para el caso particular del servidor ISC, a partir de su versión 4.3 existe una directiva que identifica a los clientes exclusivamente por la dirección MAC de la tarjeta (y rompe, por lo tanto, con la norma):
ignore-client-uids on;
La descripción completa del protocolo se encuentra en el RFC 2131.
dnsmasq es un pequeño servidor dhcp y dns que, para configuraciones sencillas, cumple sobradamente. Es preferible usarlo, por su sencillez y ligereza frente al tandem bind9+isc, si:
Para instalarlo basta con:
# aptitude install dnsmasq
Todas las configuraciones se hacen dentro del fichero /etc/dnsmasq.conf
Consulte cómo se hace en los apuntes sobre dns.
POR ESCRIBIR: interfaces de servicio, rango de ips, ips estáticas, opciones más habituales, otras opciones con RFC 2132.
Basta con instalar el paquete isc-dhcp-server:
# aptitude install isc-dhcp-server
Toda la configuración se hace tocando el fichero /etc/dhcp/dhcpd.conf. Por supuesto, siempre después de cambiar la configuración hay que reiniciar el servidor:
# /etc/init.d/isc-dhcp-server restart
Vamos a ver primero cómo configurar un servidor dhcp que, simplemente, asigne un rango de direcciones ip:
El fichero es bastante elocuente:
Consideremos que tenemos una red algo más ordenada que la anterior: las 32 primeras ip las queremos reservar para ip fijas que no participarán en las asignaciones del servidor; las 32 siguientes serán ip para equipos conocidos de la red (que sabemos reconocer por su mac) a los que asignaremos siempre la misma ip; las siguientes 32 para equipos también conocidos pero a los que no se asignará una ip en particular; y, por ultimo, otras 64 para equipos que pueden conectar circunstancialmente (portátiles, por ejemplo):
Observe que la declaración de equipos se hace a través de directivas host incluidas dentro de una directiva group. Estas declaraciones podrían contrarse fuera de la directiva subnet y, de hecho, el nombre con el que hacemos acompañar la palabra host debe ser único.
La forma de asignar distintos rangos a los distintos tipos de máquinas es mediante la directiva pool:
pool { # Maquinas conocidas con ip dinamica allow known-clients; range 192.168.1.255.64 192.168.1.255.95; } pool { # Maquinas desconocidas deny known-clients; range 192.168.1.255.96 192.168.1.255.159; }
Obsérvese la forma que se tiene de indicar a qué máquinas corresponde cada rango de ips: mediante las directivas allow y deny (que pueden utilizarse repetidamente dentro de un mismo pool. Para que una máquina reciba ip del pool debe pertenecer a alguno de los conjuntos indicados con allow y no pertenecer a ninguno de los referenciados con deny.
Es muy útil a veces definir clases según algún criterio que sirve para crear grupos de máquinas a las que luego se les define alguna directiva. Por ejemplo, supongamos que tenemos dos tipos de ordenadores distintos, blancos y negros, con distinto hardware, de manera que el fabricante de las tarjetas de uno y otro es diferente. Esto significa que las tres primeras parejas de cifras hexadecimales (que identifican al fabricante) serán iguales en cada tipo de ordenador y distintas entre ordenadores de distinta clase.
Obsérvese que en las direcciones mac las letras son minúsculas y los ceros a las izquierda deben eliminarse: 00:01:34:02:45:03, debe escribirse como 0:1:34:2:45:3. Esto se debe a que se ha convertido la variable binaria hardware a una cadena de texto y la conversión de binario a hexadecimal tiene esos inconvenientes. Para el caso particular de esta variable, se puede no hacer la conversión y escribir directamente:
match if substring(hardware,1,3) = 00:AB:22;
en vez de:
match if binary-to-ascii(16,8,":",substring(hardware,1,3)) = "0:ab:22";
Obsérvese que no hay comillas porque no es una cadena. Con todo queda por explicar, qué es eso de substring y sus dos parámetros numéricos. substring extrae el número de bytes expresados por el segundo parámetro desde el byte expresado en el primer parámetro. O sea que de la variable hardware se extraen los bytes 1,2 y 3. Sin embargo, el primer byte es el 0, no el 1. ¿por qué los parámetros no son 0,2? La razón están en que hardware antepone a la dirección MAC un byte (01) para indicar que la dirección es de type ethernet.
El servidor proporciona también el concepto de subclase que no es más que una forma más legible de escribir las clases. Supóngase que tenemos una clase llamada abirragada que está constituida por ordenador con tarjetas de tres fabricantes. Esto podría escribirse así:
class "abigarrada" { match if ( substring(hardware,1,3) = 00:AB:22 or substring(hardware,1,3) = 00:AB:23 or substring(hardware,1,3) = 00:AB:24 ); #Opciones particulares para esta clase, si se considera oportuno... }
Obsérvese el tedio de tener que estar escribiendo siempre la misma característica en la cláusula match if. Precisamente para evitar esto y hacer más legible el fichero existen las subclases:
class "abigarrada" { match substring(hardware,1,3); } subclass "abigarrada" 00:AB:22; subclass "abigarrada" 00:AB:23; subclass "abigarrada" 00:AB:24;
Además esta sintaxis permite incluir distintas opciones para cada una de las subclases:
subclass "abigarrada" 00:AB:23 { lease limit 4; }
Esto, por ejemplo, limitaría a 4 las ips que se conceden a las máquinas de esta subclase. Por supuesto, se podría haber establecido cualquiera de las opciones ya vistas (p.e. max-lease-time).
Del modo en que hemos configurado el servidor, todos los mensajes de error se almacenarán en /var/log/syslog. Ahora bien, quizás nos interese tener los apuntes en un fichero independiente. Para ello podemos añadir en dhcpd.conf la siguiente línea:
log-facility local7;
Esto hará que puedan identificarse las líneas de log que el servidor dhcp envía al rsyslog (el servicio de logs) con el prefijo local7. Ahora toca indicar que estas líneas se apunten en un fichero independiente. Para ello puede hacerse lo siguiente
# echo "local7.* -/var/log/dhcpd.log" > /etc/rsyslog.d/dhcp.conf
lo cual originará que todos los mensajes del servidor dhcp se almacenen en /var/log/dhcpd.log. Ahora bien, esto no impide que los mensajes se sigan almacenando también en /var/log/syslog. Si queremos quitarlos de ahí hay que editar el fichero general /etc/rsyslog.conf y añadir lo marcado en negrita:
*.*;auth,authpriv.none,local7.none -/var/log/syslog
Si tal línea no se encuentra en rsyslog.conf es posible entonces que se encuentre dentro de uno de los ficheros del directorio /etc/rsyslog.d.
Por supuesto, habrá que reiniciar los servidores:
# invoke-rc.d isc-dhcp-server restart # invoke-rc.d rsyslog restart
Con esto tendremos los mensajes en fichero aparte. Puede ocurrir que nos interese que el servidor apunte algunos sucesos, sobre todo cuando estamos depurando nuestra configuración. Esto se hace con la cláusula log. Por ejemplo, imaginemos que queremos hacer una clase, pero no tenemos muy claro si la cláusula match va a realizar bien el filtrado o no. En este caso, puede recurrirse a algo así:
class "especialitos" { match if substring(hardware,1,3) = 00:11:22; log(info,concat("El ordenador con MAC ",binary-to-ascii(16,8,":",substring(hardware,1,6))," es de la clase especialitos")); }
Con esto, cada vez que el servidor dhcp reciba la petición de un cliente especialito lo apuntará en el fichero de log.
También puede ser útil observar qué ips entrega el servidor, mirando el fichero /var/lib/dhcp/dhcpd.leases. No solamente se pueden consultar las ips entregadas, sino también el periodo de concesión. Si lo que se quiere es comprobar qué parámetros entrega el servidor al cliente (lo cual es muy útil si, por ejemplo, estamos montando un servidor pxe con http), entonces hay que mirar el fichero /var/lib/dhcp/dhclient.leases en el ordenador cliente (no en el servidor).
A veces es interesante poder identificar el tipo de máquina cuando esta pide una dirección ip al servidor. Es posible manipular el cliente para que este envíe la opción user-class:
class "manipuladas" { match if option user-class = "Maquinas Clientes"; }
pero esto implica hacer una configuración en el cliente; y no siempre es posible, bien porque no tenemos acceso a él, bien porque el cliente esté arrancando por red y no llegue a cargar el sistema operativo en cuyo cliente dhcp hemos hecho la configuración.
Para todos estos casos tenemos la posibilidad de consultar tres datos que el cliente envía al servidor:
class "android" { match if substring(option vendor-class-identifier,0,7) = "dhcpd-"; }Desgraciadamente, no todos los clientes envían esta información al servidor.
class "apple" { match binary-to-ascii(10,8,",",option dhcp-parameter-request-list); } subclass "apple" "1,3,6,15,119,78,79,95,252"; subclass "apple" "1,3,6,15,119,252"; subclass "apple" "1,3,6,15,119,252,46,208,92"; subclass "apple" "1,3,6,15,119,252,67,52,13";
Una de los grandes problemas con los que nos podemos encontrar si decidimos configurar dentro de nuestra red un acceso wifi abierto o semiabierto (es decir, que tenga contraseña, pero sea conocida por todos) es la gran cantidad de smartphones que pueden llegar a conectarse. Si deseamos que este acceso wifi, no sea lúdico, sino de trabajo, y que se puedan conectar a él ordenadores portátiles, pero no teléfonos móviles, es necesario habilitar un mecanismo que nos permita filtrarlos. No existe una solución completa, pero sí podemos ponérselo difícil al usuario.
Las acciones estratégicas para lograr nuestro propósito son:
Por tanto, hay diversos caminos para alcanzar nuestro próposito, cada uno de los cuales tiene sus ventajas y sus inconvenientes:
Sea como sea, ha de tenerse claro que el método no es infalible, puesto que se basa en que el servidor DHCP sea capaz de realizar la identificación. Así pues, hay dos modos de burlarlo:
Ninguna de estas dos prevenciones es algo que comúnmente se haga y, de hecho, no son fáciles de realizar, sobre todo la segunda.
Se basa en crear una clase a la que pertenezcan los dispostivos móviles. las condiciones de pertenencia, dado que el mercado de estos dispositiovs es bastante dinámico, conviene revisarlas de vez en cuando:
class "smartphones" { match if not known and ( # ANDROID # substring(option host-name,0,8) = "android-" or substring(option vendor-class-identifier,0,7) = "dhcpcd-" or # BLACKBERRY # substring(option host-name,0,11) = "BLACKBERRY-" or binary-to-ascii(10, 8, ",", option dhcp-parameter-request-list)= "56,6,1,3,15" or # IPHONE binary-to-ascii(10, 8, ",", substring(option dhcp-parameter-request-list,0,6))= "1,3,6,15,119,252" or binary-to-ascii(10, 8, ",", option dhcp-parameter-request-list)= "1,3,6,15,119,78,79,95,252" or # NOKIA binary-to-ascii(10, 8, ",", option dhcp-parameter-request-list)= "1,28,2,121,3,15,6,12" or binary-to-ascii(10, 8, ",", option dhcp-parameter-request-list)= "1,3,6,28" or binary-to-ascii(10, 8, ",", option dhcp-parameter-request-list)= "1,3,6,12,15,17,28,40,41,42" ); ignore booting; execute("ipset", "-exist", "add", "bannedmacs", binary-to-ascii (16, 8, ":", substring(hardware, 1, 6))); }
La declaración de la clase está compuesta por tres instrucciones:
# ipset -exist add bannedmacs IP:DEL:CL:IEN:TEEl comando apunta en un conjunto llamado bannedmacs la MAC del cliente indeseable. Obviamente este conjunto debe existir previamente, así que deberíamos haber ejecutado con anterioridad una orden parecida a esta:
# ipset create bannedmacs hash:mac timeout $((30*60))que crea un conjunto hash:mac cuyos registros tienen una dureción de media hora. De este modo, de todo dispositivo móvil que intente obtener una dirección ip, quedará registrada la MAC durante media hora (o lo que más nos convenga).
Si nuestro propósito es filtrar sin más los dispositivos, podríamos estar tentados de pensar que con lo hecho bajo el epígrafe anterior ya está resuelto el reto y que, incluso la directiva execute, sobra. Pero no es así, puesto que negar una dirección ip, no veta al dispositivo, y, de hecho, si algún usuario inquieto decide fijar una dirección ip a mano, comprobará que puede acceder a la red sin problemas. Esa es la razón por la que se registran las direcciones mac de estos dispositivos. Con ellas podemos denegar el acceso escribiendo un par de reglas de iptables:
# iptables -A FORWARD -i eth1 -m set --match-set bannedmacs src -j DROP # iptables -A INPUT -i eth1 -m set --match-set bannedmacs src -j DROP
La penalización es bastante más complicada que el simple filtrado, ya que exige crear un control del tráfico en la interfaz
de salida hacia internet. Por lo pronto, es necesario eliminar la segunda de las directivas (ignore booting;
)
para que los dispositivos puedan obtener una ip. Además, es conveniente sustituirla por otra que limite el número
de dispositivos a los que se les concede una ip, para que nunca ocupen todas las disponibles:
lease limit 25;
Por ejemplo, 25. Limitar el número de ips concedidas de esta forma es la configuración óptima. Sin embargo, en las últimas versiones del servidor dhcp, hay un bug que inutiliza esta directiva, de modo que cuando se alcanza el límite, el servidor se cae. Mientras este bug no se soluciona, la única alternativa es no incluir la directiva y asociar la clase con un rango limitado de direcciones ip (véase la directiva pool). Esta remiendo temporal tiene el inconveniente de que cuando se agota el rango asignado, la directiva execute deja de ejecutarse y, como consecuencia, dejan de registrarse las mac.
La directiva execute conviene también revisarla. Como en este caso se concede ip, si el tiempo de concesión es mayor que el tiempo de permanencia de la MAC en el conjunto (timeout), resultará que pasado un tiempo de conexión (el timeout) y hasta que se renuevela concesión los smartphones no se verán penalizados. Podríamos asegurarnos de que el timeout es mayor, pero existe otra solución más elegante: prescindir del timeout en el conjunto, lo que hará permanentes las MAC, y añadirlas o eliminarlas según los clientes reciban ip o la revoquen o les caduque la concesión:
on commit { execute("ipset", "-exist", "add", "bannedmacs", binary-to-ascii (16, 8, ":", substring(hardware, 1, 6))); } on release { execute("ipset", "-exist", "del", "bannedmacs", binary-to-ascii (16, 8, ":", substring(hardware, 1, 6))); } on expiry { execute("ipset", "-exist", "del", "bannedmacs", binary-to-ascii (16, 8, ":", substring(hardware, 1, 6))); }
Recuérdese que es importante que en este caso se cree el conjunto sin tiempo de caducidad:
# ipset create bannedmacs hash:mac
Resuelto el problema de registrar cuáles son los smartphones que detecta el servidor DHCP, toca preocuparse de penalizarlos, y esto segundo depende de si las conexiones atraviesan directamente el servidor o si por el contrario lo hacen a través de un servidor proxy como squid. Incluso puede darse el caso de que parte del tráfico circule directamente y parte (el tráfico web) a través del proxy:
Obsérvese en el esquema que, a la salida del tráfico por eth0, se conoce la procedencia del tráfico que no pasa por squid. En cambio, el tráfico web procede todo de squid, sea o no penalizado. La estrategia consiste en asignar dos direcciones a la interfaz de salida eth0: con la principal (172.22.0.2) se enmascarará el tráfico no penalizado, mientras que con la dirección secundaria (172.22.255.2) se enmascarará el tráfico penalizado. Hecho esto, se puede crear un control de tráfico con tc a la salida de eth0 que provoque la penalización.
Lograr enmascarar con una u otra dirección el tráfico que no gestiona squid es relativamente sencillo:
# ip addr add 172.22.255.2/16 dev eth0 # iptables -t nat -I POSTROUTING -o eth0 -m set --match-set bannedmacs src -j SNAT --to-source 172.22.255.2
En cambio, la penalización del tráfico gestionado por squid no se resuelve con la anterior regla de iptables, puesto que el origen de ese tráfico es el propio servidor (squid) y no los clientes que originan las peticiones. En este caso, debemos lograr que squid identifique las peticiones que proceden de los smartphones. Sin embargo, no podemos usar el módulo set dentro de él, así que la estrategia pasa por reservar una ip de la red a la que pertenecen la wifi y, antes de que los paquetes lleguen a squid, fingir que proceden de ella. Por ejemplo, si la red wifi es la 192.168.255.0/24, podemos hacer que la dirección 192.168.255.254 nunca sea asignada por el servidor dhcp a ningún cliente y usarla para nuestro propósito:
# iptables -t nat -A INPUT -p tcp --dport 8080 -m set --match-set bannedmacs src -j SNAT --to-source 192.168.255.254
Hemos supuesto que squid escucha por el puerto 8080.
Por último, en squid podemos hacer que el tráfico que procede de esta ip salga del programa hacia internet con una marca (0x1, por ejemplo):
acl smartphones src 192.168.255.254 tcp_outgoing_mark 0x1 smartphones
Y, finalmente, si hacemos que el tráfico con esta marca se enmascare con la dirección secundaria...
# iptables -t nat -I POSTROUTING -o eth0 -m mark --mark 0x1 -j SNAT --to-source 172.22.255.2
sufrirá las penalizaciones que hemos reservado a los smartphones.