
Me encanta automatizar el proceso y escribir mis propias bicicletas para estudiar este o aquel material. Mi nuevo objetivo era un servidor DHCP, que emitirá una dirección en redes pequeñas para que se pueda realizar la configuración inicial del equipo.
En este artículo hablaré un poco sobre el protocolo DHCP y algunas sutilezas de bash.
Resultado final
Comencemos desde el final, para que quede claro por qué luchamos.
Demostración de trabajo:

Repositorio con script:
firemoon777 / bash-dhcp-serverProblema inicial
La configuración que necesito se realiza de esta manera: nos conectamos directamente mediante un cable de par trenzado al equipo, emitimos una dirección temporal a través de DHCP y lo configuramos con un script ya creado. Y así, de diez a veinte veces seguidas.
Para muchos, el conocido servidor isc-dhcp hace su trabajo perfectamente, pero, por desgracia, no notifica a mi script que se emite la dirección, por lo que debe bloquear de alguna manera la ejecución hasta que se emita la dirección.
Parece que la solución está en la superficie: haga ping hasta que quede azul en la cara, hasta que el equipo responda:
while ! ping -c1 -W1 "$DHCP" | grep -q "time=" do echo "Waiting for $DHCP..." done
Pero esta decisión definitivamente carece de aventurerismo.
Parte teórica
Obtener una dirección con un único servidor DHCP
El protocolo DHCP funciona a través de UDP en los puertos 67 y 68. El servidor siempre funciona solo en 67 y el cliente solo en 68. Dado que el cliente no tiene una dirección (tiene la dirección 0.0.0.0), los paquetes DHCP se transmiten. Es decir el cliente siempre envía paquetes a la dirección 255.255.255.255:67 desde la dirección 0.0.0.0:68, y el servidor envía desde su dirección: 67 a la dirección 255.255.255.255:68.
El cliente
recibe la dirección en cuatro paquetes (
DORA ):
- El cliente descubre dónde está el servidor DHCP ( D iscover)
- El servidor responde y ofrece su dirección ( O ffer)
- El cliente solicita la dirección propuesta de un servidor específico (Solicitud)
- El servidor acepta y emite la dirección ( A ck)
Visualmente, el esquema se puede representar de la siguiente manera:

Obtener una dirección con múltiples servidores DHCP
Cuando el cliente envía Discover, todos los servidores que pueden escuchar envían su oferta al cliente. Pero el cliente debe elegir uno. La selección del cliente se anuncia en el mensaje Solicitud con la opción 54 (servidor DHCP), que contiene la dirección IP del servidor DHCP preferido. Aunque la Solicitud también se envía a todos en la red, solo responderá el servidor DHCP cuya IP se especifica en la opción 54.

Contenido del paquete DHCP
Un paquete DHCP consta de dos partes: una constante, 236 bytes de tamaño y una variable que lleva opciones (Opción DHCP).
Tabla con todos los campos del paquete DHCP de WikipediaEl campo | Descripción | Longitud (en bytes) |
---|
op
| Tipo de mensaje Por ejemplo, puede tomar valores: BOOTREQUEST (0x01, solicitud del cliente al servidor) y BOOTREPLY (0x02, respuesta del servidor al cliente).
| 1
|
htype
| Tipo de dirección de hardware. Los valores válidos para este campo se definen en los números asignados RFC 1700. Por ejemplo, para una dirección MAC de Ethernet, este campo se establece en 0x01.
| 1
|
hlen
| La longitud de la dirección de hardware en bytes. La dirección MAC de Ethernet es 0x06.
| 1
|
lúpulo
| El número de enrutadores intermedios (los llamados agentes de retransmisión DHCP ) a través de los cuales pasó el mensaje. El cliente establece este campo en 0x00.
| 1
|
xid
| Un identificador de transacción único de 4 bytes generado por el cliente al comienzo del proceso de obtención de la dirección.
| 4 4
|
segundos
| El tiempo en segundos desde el inicio del proceso de obtención de la dirección. No se puede usar (en este caso se establece en 0x0000).
| 2
|
banderas
| El campo para banderas es parámetros especiales del protocolo DHCP.
| 2
|
ciaddr
| Dirección IP del cliente. Se completa solo si el cliente ya tiene su propia dirección IP y puede responder a las solicitudes ARP (esto es posible si el cliente realiza el procedimiento para actualizar la dirección después de que expire el contrato de arrendamiento).
| 4 4
|
yiaddr
| La nueva dirección IP del cliente propuesta por el servidor.
| 4 4
|
siaddr
| Dirección IP del servidor. Devuelto en la cláusula DHCP (ver más abajo).
| 4 4
|
giaddr
| La dirección IP del agente de retransmisión, si uno estuvo involucrado en el proceso de entregar el mensaje DHCP al servidor.
| 4 4
|
chaddr
| La dirección de hardware (generalmente la dirección MAC) del cliente.
| 16
|
nombre
| Nombre del servidor opcional como una cadena terminada en nulo.
| 64
|
archivo
| Un nombre de archivo de servidor opcional utilizado por estaciones de trabajo sin disco al descargar de forma remota. Al igual que sname , se representa como una cadena terminada en nulo.
| 128
|
opciones
| Campo de opciones de DHCP . Aquí se indican varias opciones de configuración adicionales. Al comienzo de este campo, se indican cuatro bytes especiales con valores 99, 130, 83, 99 ("números mágicos"), lo que permite al servidor determinar la presencia de este campo. El campo tiene una longitud variable, pero el cliente DHCP debe estar listo para recibir un mensaje DHCP de 576 bytes (en este mensaje, el campo de opciones tiene una longitud de 340 bytes).
| variable
|
Lista de todas las opciones de DHCP en RFC 2132Las opciones de DHCP se codifican de la siguiente manera:
Por ejemplo, el parámetro 3 (puerta de enlace propuesta) con un valor de 10.0.0.1:
En caso de que necesite pasar varios parámetros, la longitud del parámetro aumenta.
Por ejemplo, en el parámetro 6 (servidor DNS) transmitiremos dos direcciones (1.1.1.1 y 8.8.4.4):
Una señal del final del campo de opción es un parámetro con el número 255 (0xFF) y una longitud de 0.
Muy a menudo, el cliente coloca el parámetro 55 (una lista de parámetros que quiere recibir en respuesta) en DHCP Discover, sin embargo, tenemos el derecho de darle no todo lo que solicitó.
Parte práctica
Originalmente se planeó escribir el servidor en un lenguaje más adecuado (C) para esto, sin embargo, sería mundano y simple. Se trata de escribir un script que se hará cargo de las funciones del servidor dhcp.
Simplificación
Como se suponía que el servidor que se estaba desarrollando se utilizaría en redes de dos nodos conectados por un parche, se adoptaron las siguientes simplificaciones:
- garantizado que un cliente en la red;
- se garantiza que no hay más servidores dhcp en la red
- el iniciador decide qué dirección emitir
- La versión de DHCP y la disminución de DHCP se ignoran
Oyente
En primer lugar, debe aprender a recibir paquetes. Esto requiere un oyente
comprensivo certificado , por ejemplo, nc. Pero no todos los nc son adecuados para estos fines. OpenBSD netcat 1.130 de Debian es adecuado, pero 1.105 con Ubuntu se ha ido. Ejecute nc para escuchar todos los paquetes UDP que lleguen al puerto 67.
nc -l 0.0.0.0 -up 67 -w0
OpenBSD netcat también es necesario debido al modificador -w con un valor de 0. Después de recibir un paquete (UDP Broadcast), el nc tradicional no recibe más paquetes, pero no termina.
Manejo de bytes sin procesar
En el shell, es muy difícil trabajar con caracteres no imprimibles, como un carácter nulo: simplemente lo ignora. Un paquete DHCP contiene muchos bytes 0x00 (por ejemplo, el campo del archivo). La solución al problema viene en forma de volcado hexadecimal:
nc -l 0.0.0.0 -up 67 -w0 | stdbuf -o0 od -v -w1 -t x1 -An
Un byte por línea, sin generar la dirección, sin omitir bytes duplicados. También puede darle vida a stdbuf -o0 para que la salida no esté almacenada.
Recepción, almacenamiento y procesamiento de paquetes.
Desde el comando od stdout, el comando de lectura toma los bytes y los agrega a una matriz.
msg=() for i in {0..235}; do read -r tmp msg[$i]=$tmp done
Aunque todos los valores se transmiten en notación hexadecimal, el número de opción DHCP y la longitud de la opción se muestran mejor en la pantalla / en los registros en la forma decimal habitual. Para hacer esto, puede usar una entrada corta bash'a:
$ op=AC $ echo $((16
El paquete recibido se edita según el tipo de solicitud (Descubrir o Solicitar) y se devuelve.
Respuesta
Sin embargo, enviar un paquete no es una tarea tan fácil. Primero necesita convertir bytes del volcado a bytes sin procesar y enviar todo de una vez con un paquete.
La conversión se puede hacer con la utilidad printf usando secuencias de escape. Y para que no se pierda nada, escriba bytes inmediatamente en un archivo.
OpenBSD netcat también se utiliza para enviar. Sin embargo, si la versión 1.105 con Ubuntu es adecuada como escucha, no es adecuada para transmitir mensajes UDP: obtenemos el error de protocolo no disponible.
cat /tmp/dhcp.payload | nc -ub 255.255.255.255 68 -s $SERVER -p 67 -w0
El modificador -b permite enviar mensajes de difusión, y esta es la segunda razón por la cual el servidor debe ejecutarse desde debajo del superusuario.
¿Cuáles son las limitaciones?
Este servidor DHCP fue diseñado con simplificaciones como un solo cliente en la red. Sin embargo, funcionará con varios clientes. Solo obtén la dirección más rápida.

Conclusión
Aunque los scripts de bash difícilmente pueden llamarse un lenguaje de programación completo, sin embargo, con el debido deseo, incluso puede resolver problemas como emitir una dirección IP en la red sin usar un software especialmente diseñado para esto. Y resolver problemas específicos no solo trae alegría, sino también nuevos conocimientos que se abrieron en el momento de la solución.
Fuentes
- DHCP - Wikipedia
- Parámetros DHCP y BOOTP - IANA