
El objetivo de este proyecto fue:
- El estudio del protocolo DHCP en la red IPv4.
- Estudio de Python (un poco más que desde cero 😉 ).
- reemplazo del servidor. (mi fork), original. , que resulta cada vez más difícil compilar para un nuevo sistema operativo. Y no me gusta que el binario no se pueda "cambiar en este momento".
- obtener un servidor DHCP funcional con la posibilidad de seleccionar la dirección IP del suscriptor según la MAC del suscriptor o la combinación MAC del switch + puerto (Opción 82).
- escribir otro proyecto similar (¡Oh! es mi actividad favorita).
- recibir críticas sobre mi torpeza en HabrHabr (o mejor aún, una invitación) 😉.
Resultado: funciona 😉. Probado en FreeBSD y Ubuntu. Teóricamente, el código podría ser solicitado para funcionar en cualquier sistema operativo, ya que en el código no parecen haber enlaces específicos.
¡Cuidado! Hay mucho más adelante.
Enlace al repositorio para los amantes de .
El proceso de instalación, configuración y uso del resultado del "estudio teórico" se encuentra más abajo, y a continuación un poco de teoría sobre el protocolo DHCP. Para mí. Y para la historia 😉.
Un poco de teoría.
¿Qué es el DHCP?
Es un protocolo de red que permite a un dispositivo conocer su dirección IP (así como otros parámetros como puerta de enlace, DNS, etc.) desde el servidor DHCP. El intercambio de paquetes se realiza a través del protocolo UDP. El principio general de funcionamiento del dispositivo al solicitar parámetros de red es el siguiente:
- El dispositivo (cliente) envía una solicitud UDP de difusión (DHCPDISCOVER) por toda la red con la petición: "bueno, alguien, ¿me da una dirección IP?". De hecho, normalmente (aunque no siempre) la solicitud se realiza desde el puerto 68 (fuente), mientras que el destino es el puerto 67 (destino). Algunos dispositivos también envían paquetes desde el puerto 67. Dentro del paquete DHCPDISCOVER está incluida la dirección MAC del dispositivo cliente.
- Todos los servidores DHCP en la red (y puede haber varios) forman para el dispositivo que envió el DHCPDISCOVER, una oferta DHCPOFFER con la configuración de red y también la envían de forma amplia por la red. La identificación de a quién está destinado este paquete se realiza por la dirección MAC del cliente proporcionada anteriormente en la solicitud DHCPDISCOVER.
- El cliente recibe paquetes con ofertas de configuración de red, elige la más atractiva (los criterios pueden variar, por ejemplo, el tiempo de entrega del paquete, la cantidad de rutas intermedias) y realiza una «solicitud oficial» DHCPREQUEST al servidor DHCP que le guste con la configuración de red. En este caso, el paquete va hacia un servidor DHCP específico.
- El servidor que recibe el DHCPREQUEST envía un paquete en formato DHCPACK, que enumera nuevamente la configuración de red destinada a este cliente.

Además, existen paquetes DHCPINFORM que son enviados por el cliente, cuyo objetivo es informar al servidor DHCP que el «cliente está vivo» y está utilizando la configuración de red proporcionada. En la implementación de este servidor, estos paquetes son ignorados.
Formato de los paquetes
En general, un marco de paquete Ethernet se ve aproximadamente así:
![]()
En nuestro caso, examinaremos solo los datos contenidos directamente en el paquete UDP, sin las cabeceras de los protocolos de los niveles OSI, es decir, la estructura DHCP:
DHCPDISCOVER
Así, el proceso de obtención de una dirección IP para un dispositivo comienza cuando el cliente DHCP envía una consulta de difusión desde el puerto 68 a 255.255.255.255:67. En este paquete, el cliente incluye su dirección MAC, así como lo que desea recibir del servidor DHCP. La estructura del paquete se describe en la tabla a continuación.
Tabla de estructura del paquete DHCPDISCOVER
Posición en el paquete
Nombre del valor
Ejemplo
Representación
Byte
Explicación
1
Boot Request
1
Hex
1
Tipo de mensaje. 1 — solicitud del cliente al servidor, 2 — respuesta del servidor al cliente.
2
Tipo de hardware
1
Hex
1
Tipo de dirección de hardware, en este protocolo 1 — MAC.
3
Longitud de la dirección de hardware
6
Hex
1
Longitud de la dirección MAC del dispositivo.
4
Saltos
1
Hex
1
Número de rutas intermedias.
5
ID de transacción
23:cf:de:1d
Hex
4
Identificador único de transacción. Lo genera el cliente al inicio de la operación de solicitud.
7
Segundos transcurridos
0
Hex
4
Tiempo en segundos desde el inicio del proceso de obtención de la dirección.
9
Flags de Bootp
0
Hex
2
Algunos flags que pueden establecerse como indicación de parámetros del protocolo.
11
Dirección IP del cliente
0.0.0.0
Cadena
4
Dirección IP del cliente (si existe).
15
Su dirección IP de cliente
0.0.0.0
Cadena
4
Dirección IP propuesta por el servidor (si existe).
19
Dirección IP del siguiente servidor
0.0.0.0
Cadena
4
Dirección IP del servidor (si se conoce).
23
Dirección IP del agente de retransmisión
172.16.114.41
Cadena
4
Dirección IP del agente de retransmisión (por ejemplo, el switch).
27
Dirección MAC del cliente
14:d6:4d:a7:c9:55
Hex
6
Dirección MAC del remitente del paquete (cliente).
31
Relleno de dirección de hardware del cliente
Hex
10
Espacio reservado. Normalmente llenado con ceros.
41
Nombre del host del servidor
Cadena
64
Nombre del servidor DHCP. Generalmente no se transmite.
105
Nombre del archivo de arranque.
Cadena
128
Nombre del archivo en el servidor, utilizado por las estaciones sin disco durante el arranque
235
Cookie mágica
63:82:53:63
Hex
4
Número "mágico" que permite, entre otras cosas, determinar que este paquete pertenece al protocolo DHCP
Opciones de DHCP. Pueden estar en cualquier orden
236
Número de opción
53
Dec
1
Opción 53, que define el tipo de paquete DHCP
1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM
Longitud de la opción
1
Dec
1
Valor de la opción
1
Dec
1
Número de opción
50
Dec
1
¿Qué dirección IP desea obtener el cliente?
Longitud de la opción
4
Dec
1
Valor de la opción
172.16.134.61
Cadena
4
Número de opción
55
1
Parámetros de red solicitados por el cliente. Pueden ser diversos
01 — Máscara de red
03 — Puerta de enlace
06 — DNS
oc — Nombre del host
0f — nombre del dominio de la red
1c — dirección de la solicitud de difusión (broadcast)
42 — nombre del servidor TFTP
79 — Ruta estática sin clase
Longitud de la opción
8
1
Valor de la opción
01:03:06:0c:0f:1c:42:79
8
Número de opción
82
Dec
Opción 82, que transmite la dirección MAC del dispositivo — retransmisor y algunos valores adicionales.
Por lo general, el puerto del switch en el que opera el cliente final de DHCP. En esta opción se "inserta" información adicional. El primer byte es el número de la "subopción", el segundo su longitud, luego su valor.
En este caso, en la opción 82 se insertan las subopciones:
Agent Circuit ID = 00:04:00:01:00:04, donde los últimos dos bytes son el puerto del cliente DHCP desde el cual se recibió la solicitud
Agent Remote ID = 00:06:c8:be:19:93:11:48 — dirección MAC del dispositivo retransmisor DHCP
Longitud de la opción
18
Dec
Valor de la opción
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
Fin del paquete
255
Dec
1
255 simboliza el final del paquete
DHCPOFFER
Tan pronto como el servidor recibe un paquete DHCPDISCOVER y si ve que puede ofrecer algo al cliente de lo solicitado, forma una respuesta para él — DHCPDISCOVER. La respuesta se envía al puerto "de donde vino", como difusión, ya que en ese momento el cliente aún no tiene dirección IP, por lo tanto, solo puede aceptar el paquete si se envía de manera amplia. El cliente reconoce que este es un paquete para él por su dirección MAC dentro del paquete, así como por el número de transacción que genera al crear el primer paquete.
Tabla de estructura del paquete DHCPOFFER
Posición en el paquete
Nombre del valor (comúnmente aceptado)
Ejemplo
Representación
Byte
Explicación
1
Boot Request
1
Hex
1
Tipo de mensaje. 1 — solicitud del cliente al servidor, 2 — respuesta del servidor al cliente.
2
Tipo de hardware
1
Hex
1
Tipo de dirección de hardware, en este protocolo 1 — MAC.
3
Longitud de la dirección de hardware
6
Hex
1
Longitud de la dirección MAC del dispositivo.
4
Saltos
1
Hex
1
Número de rutas intermedias.
5
ID de transacción
23:cf:de:1d
Hex
4
Identificador único de transacción. Lo genera el cliente al inicio de la operación de solicitud.
7
Segundos transcurridos
0
Hex
4
Tiempo en segundos desde el inicio del proceso de obtención de la dirección.
9
Flags de Bootp
0
Hex
2
Algunas banderas que pueden establecerse como indicación de parámetros del protocolo. En este caso, 0 significa tipo de solicitud Unicast
11
Dirección IP del cliente
0.0.0.0
Cadena
4
Dirección IP del cliente (si existe).
15
Su dirección IP de cliente
172.16.134.61
Cadena
4
Dirección IP propuesta por el servidor (si existe).
19
Dirección IP del siguiente servidor
0.0.0.0
Cadena
4
Dirección IP del servidor (si se conoce).
23
Dirección IP del agente de retransmisión
172.16.114.41
Cadena
4
Dirección IP del agente de retransmisión (por ejemplo, el switch).
27
Dirección MAC del cliente
14:d6:4d:a7:c9:55
Hex
6
Dirección MAC del remitente del paquete (cliente).
31
Relleno de dirección de hardware del cliente
Hex
10
Espacio reservado. Normalmente llenado con ceros.
41
Nombre del host del servidor
Cadena
64
Nombre del servidor DHCP. Generalmente no se transmite.
105
Nombre del archivo de arranque.
Cadena
128
Nombre del archivo en el servidor, utilizado por las estaciones sin disco durante el arranque
235
Cookie mágica
63:82:53:63
Hex
4
Número "mágico" que permite, entre otras cosas, determinar que este paquete pertenece al protocolo DHCP
Opciones de DHCP. Pueden estar en cualquier orden
236
Número de opción
53
Dec
1
Opción 53, que define el tipo de paquete DHCP 2 — DHCPOFFER
Longitud de la opción
1
Dec
1
Valor de la opción
2
Dec
1
Número de opción
1
Dec
1
Opción que ofrece al cliente DHCP una máscara de red
Longitud de la opción
4
Dec
1
Valor de la opción
255.255.224.0
Cadena
4
Número de opción
3
Dec
1
Opción que ofrece al cliente DHCP una puerta de enlace predeterminada
Longitud de la opción
4
Dec
1
Valor de la opción
172.16.12.1
Cadena
4
Número de opción
6
Dec
1
Opción que ofrece al cliente DHCP un DNS
Longitud de la opción
4
Dec
1
Valor de la opción
8.8.8.8
Cadena
4
Número de opción
51
Dec
1
Tiempo de vida de los parámetros de red otorgados en segundos, después del cual el cliente DHCP debe solicitarlos nuevamente
Longitud de la opción
4
Dec
1
Valor de la opción
86400
Dec
4
Número de opción
82
Dec
1
Opción 82, repite lo que llegó en DHCPDISCOVER
Longitud de la opción
18
Dec
1
Valor de la opción
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Fin del paquete
255
Dec
1
255 simboliza el final del paquete
DHCPREQUEST
Después de que el cliente recibe el DHCPOFFER, forma un paquete con la solicitud de parámetros de red, no a todos los servidores DHCP en la red, sino solo a uno específico, cuya oferta DHCPOFFER le ha «gustado» más. Los criterios de «gusto» pueden ser diversos y dependen de la implementación del cliente DHCP. El destinatario de la solicitud se indica mediante la dirección MAC del servidor DHCP. Además, el paquete DHCPREQUEST puede ser enviado por el cliente sin haber formado antes un DHCPDISCOVER, si la dirección IP del servidor ya se había recibido anteriormente.
Tabla de estructura del paquete DHCPREQUEST
Posición en el paquete
Nombre del valor (comúnmente aceptado)
Ejemplo
Representación
Byte
Explicación
1
Boot Request
1
Hex
1
Tipo de mensaje. 1 — solicitud del cliente al servidor, 2 — respuesta del servidor al cliente.
2
Tipo de hardware
1
Hex
1
Tipo de dirección de hardware, en este protocolo 1 — MAC.
3
Longitud de la dirección de hardware
6
Hex
1
Longitud de la dirección MAC del dispositivo.
4
Saltos
1
Hex
1
Número de rutas intermedias.
5
ID de transacción
23:cf:de:1d
Hex
4
Identificador único de transacción. Lo genera el cliente al inicio de la operación de solicitud.
7
Segundos transcurridos
0
Hex
4
Tiempo en segundos desde el inicio del proceso de obtención de la dirección.
9
Flags de Bootp
8000
Hex
2
Algunas banderas que pueden establecerse como indicación de parámetros del protocolo. En este caso, se ha configurado como «broadcast».
11
Dirección IP del cliente
0.0.0.0
Cadena
4
Dirección IP del cliente (si existe).
15
Su dirección IP de cliente
172.16.134.61
Cadena
4
Dirección IP propuesta por el servidor (si existe).
19
Dirección IP del siguiente servidor
0.0.0.0
Cadena
4
Dirección IP del servidor (si se conoce).
23
Dirección IP del agente de retransmisión
172.16.114.41
Cadena
4
Dirección IP del agente de retransmisión (por ejemplo, el switch).
27
Dirección MAC del cliente
14:d6:4d:a7:c9:55
Hex
6
Dirección MAC del remitente del paquete (cliente).
31
Relleno de dirección de hardware del cliente
Hex
10
Espacio reservado. Normalmente llenado con ceros.
41
Nombre del host del servidor
Cadena
64
Nombre del servidor DHCP. Generalmente no se transmite.
105
Nombre del archivo de arranque.
Cadena
128
Nombre del archivo en el servidor, utilizado por las estaciones sin disco durante el arranque
235
Cookie mágica
63:82:53:63
Hex
4
Número "mágico" que permite, entre otras cosas, determinar que este paquete pertenece al protocolo DHCP
Opciones de DHCP. Pueden estar en cualquier orden
236
Número de opción
53
Dec
3
Opción 53, que define el tipo de paquete DHCP 3 — DHCPREQUEST
Longitud de la opción
1
Dec
1
Valor de la opción
3
Dec
1
Número de opción
61
Dec
1
Identificador del cliente: 01 (para Ethernet) + dirección MAC del cliente
Longitud de la opción
7
Dec
1
Valor de la opción
01:2c:ab:25:ff:72:a6
Hex
7
Número de opción
60
Dec
Identificador de clase del proveedor. En mi caso, indica la versión del cliente DHCP. Es posible que otros dispositivos devuelvan algo diferente. Por ejemplo, Windows reporta MSFT 5.0.
Longitud de la opción
11
Dec
Valor de la opción
udhcp 0.9.8
Cadena
Número de opción
55
1
Parámetros de red solicitados por el cliente. Pueden ser diversos
01 — Máscara de red
03 — Puerta de enlace
06 — DNS
oc — Nombre del host
0f — nombre del dominio de la red
1c — dirección de la solicitud de difusión (broadcast)
42 — nombre del servidor TFTP
79 — Ruta estática sin clase
Longitud de la opción
8
1
Valor de la opción
01:03:06:0c:0f:1c:42:79
8
Número de opción
82
Dec
1
Opción 82, repite lo que llegó en DHCPDISCOVER
Longitud de la opción
18
Dec
1
Valor de la opción
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Fin del paquete
255
Dec
1
255 simboliza el final del paquete
DHCPACK
Como confirmación de que «sí, exactamente, esta es tu dirección IP, y no la entregaré a nadie más» del servidor DHCP, es el paquete en formato DHCPACK del servidor al cliente. Se envía, al igual que los demás paquetes, de forma broadcast. Aunque, en el código a continuación del servidor DHCP implementado en Python, duplico cualquier solicitud broadcast enviando el paquete a una IP específica del cliente, si ya es conocida. Además, al servidor DHCP no le preocupa si el paquete DHCPACK llegó al cliente. Si el cliente no recibe el DHCPACK, simplemente repite el DHCPREQUEST después de un tiempo.
Tabla de estructura del paquete DHCPACK
Posición en el paquete
Nombre del valor (comúnmente aceptado)
Ejemplo
Representación
Byte
Explicación
1
Boot Request
2
Hex
1
Tipo de mensaje. 1 — solicitud del cliente al servidor, 2 — respuesta del servidor al cliente.
2
Tipo de hardware
1
Hex
1
Tipo de dirección de hardware, en este protocolo 1 — MAC.
3
Longitud de la dirección de hardware
6
Hex
1
Longitud de la dirección MAC del dispositivo.
4
Saltos
1
Hex
1
Número de rutas intermedias.
5
ID de transacción
23:cf:de:1d
Hex
4
Identificador único de transacción. Lo genera el cliente al inicio de la operación de solicitud.
7
Segundos transcurridos
0
Hex
4
Tiempo en segundos desde el inicio del proceso de obtención de la dirección.
9
Flags de Bootp
8000
Hex
2
Algunas banderas que pueden establecerse como indicación de parámetros del protocolo. En este caso, se ha configurado como «broadcast».
11
Dirección IP del cliente
0.0.0.0
Cadena
4
Dirección IP del cliente (si existe).
15
Su dirección IP de cliente
172.16.134.61
Cadena
4
Dirección IP propuesta por el servidor (si existe).
19
Dirección IP del siguiente servidor
0.0.0.0
Cadena
4
Dirección IP del servidor (si se conoce).
23
Dirección IP del agente de retransmisión
172.16.114.41
Cadena
4
Dirección IP del agente de retransmisión (por ejemplo, el switch).
27
Dirección MAC del cliente
14:d6:4d:a7:c9:55
Hex
6
Dirección MAC del remitente del paquete (cliente).
31
Relleno de dirección de hardware del cliente
Hex
10
Espacio reservado. Normalmente llenado con ceros.
41
Nombre del host del servidor
Cadena
64
Nombre del servidor DHCP. Generalmente no se transmite.
105
Nombre del archivo de arranque.
Cadena
128
Nombre del archivo en el servidor, utilizado por las estaciones sin disco durante el arranque
235
Cookie mágica
63:82:53:63
Hex
4
Número "mágico" que permite, entre otras cosas, determinar que este paquete pertenece al protocolo DHCP
Opciones de DHCP. Pueden estar en cualquier orden
236
Número de opción
53
Dec
3
Opción 53, que define el tipo de paquete DHCP 5 — DHCPACK
Longitud de la opción
1
Dec
1
Valor de la opción
5
Dec
1
Número de opción
1
Dec
1
Opción que ofrece al cliente DHCP una máscara de red
Longitud de la opción
4
Dec
1
Valor de la opción
255.255.224.0
Cadena
4
Número de opción
3
Dec
1
Opción que ofrece al cliente DHCP una puerta de enlace predeterminada
Longitud de la opción
4
Dec
1
Valor de la opción
172.16.12.1
Cadena
4
Número de opción
6
Dec
1
Opción que ofrece al cliente DHCP un DNS
Longitud de la opción
4
Dec
1
Valor de la opción
8.8.8.8
Cadena
4
Número de opción
51
Dec
1
Tiempo de vida de los parámetros de red otorgados en segundos, después del cual el cliente DHCP debe solicitarlos nuevamente
Longitud de la opción
4
Dec
1
Valor de la opción
86400
Dec
4
Número de opción
82
Dec
1
Opción 82, repite lo que llegó en DHCPDISCOVER
Longitud de la opción
18
Dec
1
Valor de la opción
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Fin del paquete
255
Dec
1
255 simboliza el final del paquete
Instalación
La instalación consiste esencialmente en instalar los módulos de Python necesarios para su funcionamiento. Se supone que MySQL ya está instalado y configurado.
FreeBSD
pkg install python3 python3 -m ensurepip pip3 install mysql-connector
Ubuntu
sudo apt-get install python3 sudo apt-get install pip3 sudo pip3 install mysql-connector
Creamos la base de datos MySQL, importamos el volcado pydhcp.sql y configuramos el archivo de configuración.
Configuración
Todos los ajustes del servidor se encuentran en un archivo en formato XML. Archivo de referencia:
0.0.0.0
255.255.255.255
192.168.0.71
8600
1
255.255.255.0
192.168.0.1
8.8.8.8
localhost
test
test
pydhcp
option_82_hex:sw_port1:20:22
option_82_hex:sw_port2:16:18
option_82_hex:sw_mac:26:40
3
select ip,mask,router,dns from users where upper(mac)=upper('{option_82_AgentRemoteId_hex}') and upper(port)=upper('{option_82_AgentCircuitId_port_hex}')
select ip,mask,router,dns from users where upper(mac)=upper('{sw_mac}') and upper(port)=upper('{sw_port2}')
select ip,mask,router,dns from users where upper(mac)=upper('{ClientMacAddress}')
insert into history (id,dt,mac,ip,comment) values (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')Ahora, más detalles sobre las etiquetas:
La sección dhcpserver describe la configuración principal para iniciar el servidor, a saber:
- host — qué dirección IP escucha el servidor en el puerto 67
- broadcast — qué IP es el broadcast para DHCPOFFER y DHCPACK
- DHCPServer — qué IP tiene el servidor DHCP
- LeaseTime — tiempo de alquiler de la dirección IP otorgada
- ThreadLimit — cuántos hilos se ejecutan simultáneamente en el procesamiento de paquetes UDP entrantes en el puerto 67. Se supone que esto ayuda en proyectos de alta carga 😉
- defaultMask, defaultRouter, defaultDNS — lo que se ofrece al cliente por defecto si la IP se encuentra en la base, pero no se especifican parámetros adicionales para él
La sección mysql:
host, username, password, basename — todo es bastante autoexplicativo. La estructura aproximada de la base de datos se encuentra en
La sección query: aquí se describen las consultas para obtener OFFER/ACK:
- offer_count — número de filas con consultas que devuelven un resultado del tipo ip, mask, router, dns
- offer_n — cadena de consulta. Si la devolución es vacía, se ejecuta la siguiente consulta offer
- history_sql — consulta que escribe, por ejemplo, en "historial de autorizaciones" del cliente
En las consultas pueden participar cualquier variable de la sección options o opciones del protocolo DHCP.
La sección options. Aquí se pone más interesante. Aquí podemos crear variables que podemos usar más adelante en la sección query.
Por ejemplo:
option_82_hex:sw_port1:20:22, esta línea de comando toma toda la cadena recibida en la solicitud DHCP de la opción 82, en formato hex, en el rango de los 20 a 22 bytes incluidos y la coloca en una nueva variable sw_port1 (puerto del switch desde donde llegó la solicitud)
option_82_hex:sw_mac:26:40, se determina la variable sw_mac, tomando el hex desde el rango 26:40
Para ver todas las posibles opciones que se pueden utilizar en las solicitudes, se puede ejecutar el servidor con la clave -d. Veremos un registro similar al siguiente:
-- se recibió un paquete DHCPINFORM en el puerto 67, de 0025224ad764, b'x91xa5xe0xa3xa5xa9-x8fx8a', ('172.30.114.25', 68)
{'ClientMacAddress': '0025224ad764',
'ClientMacAddressByte': b'x00%"Jxd7d',
'HType': 'Ethernet',
'HostName': b'x91xa5xe0xa3xa5xa9-x8fx8a',
'ReqListDNS': True,
'ReqListDomainName': True,
'ReqListPerfowmRouterDiscover': True,
'ReqListRouter': True,
'ReqListStaticRoute': True,
'ReqListSubnetMask': True,
'ReqListVendorSpecInfo': 43,
'RequestedIpAddress': '0.0.0.0',
'Vendor': b'MSFT 5.0',
'chaddr': '0025224ad764',
'ciaddr': '172.30.128.13',
'flags': b'x00x00',
'giaddr': '172.30.114.25',
'gpoz': 308,
'hlen': 6,
'hops': 1,
'htype': 'MAC',
'magic_cookie': b'cx82Sc',
'op': 'DHCPINFORM',
'option12': 12,
'option53': 53,
'option55': 55,
'option60': 60,
'option61': 61,
'option82': 82,
'option_82_byte': b'x12x01x06x00x04x00x01x00x06x02x08x00'
b'x06x00x1eXx9exb2xad',
'option_82_hex': '12010600040001000602080006001e589eb2ad',
'option_82_len': 18,
'option_82_str': "b'x12x01x06x00x04x00x01x00x06x02x08x00x06x00x1eXx9exb2xad'",
'result': False,
'secs': 768,
'siaddr': '0.0.0.0',
'sw_mac': '001e589eb2ad',
'sw_port1': '06',
'xidbyte': b'<x89}x8c',
'xidhex': '3c897d8c',
'yiaddr': '0.0.0.0'}Por lo tanto, podemos envolver cualquier variable en {} y se utilizará en la consulta SQL.
Documentemos para la historia que la dirección IP que recibió el cliente:


Iniciando el servidor
.\/pydhcpdb.py -d -c config.xml
— d modo de salida por consola DEBUG
— c <nombre_archivo> archivo de configuración
Análisis de resultados
Y ahora más detalles sobre la implementación del servidor en Python. Es una carga. Python fue aprendido 'al vuelo'. Muchos aspectos se hicieron en un estilo de: 'oh, eso funcionó'. No está optimizado y se dejó así principalmente debido a la poca experiencia en desarrollo en python. Me detendré en los aspectos más interesantes de la implementación del servidor en 'código'.
Parser de archivo de configuración XML
Se utiliza el módulo estándar de Python xml.dom. Parece simple, pero en la implementación faltaba notablemente documentación y ejemplos adecuados en línea con el uso de este módulo.
tree = minidom.parse(gconfig["config_file"])
mconfig=tree.getElementsByTagName("mysql")
for elem in mconfig:
gconfig["mysql_host"]=elem.getElementsByTagName("host")[0].firstChild.data
gconfig["mysql_username"]=elem.getElementsByTagName("username")[0].firstChild.data
gconfig["mysql_password"]=elem.getElementsByTagName("password")[0].firstChild.data
gconfig["mysql_basename"]=elem.getElementsByTagName("basename")[0].firstChild.data
dconfig=tree.getElementsByTagName("dhcpserver")
for elem in dconfig:
gconfig["broadcast"]=elem.getElementsByTagName("broadcast")[0].firstChild.data
gconfig["dhcp_host"]=elem.getElementsByTagName("host")[0].firstChild.data
gconfig["dhcp_LeaseTime"]=elem.getElementsByTagName("LeaseTime")[0].firstChild.data
gconfig["dhcp_ThreadLimit"]=int(elem.getElementsByTagName("ThreadLimit")[0].firstChild.data)
gconfig["dhcp_Server"]=elem.getElementsByTagName("DHCPServer")[0].firstChild.data
gconfig["dhcp_defaultMask"]=elem.getElementsByTagName("defaultMask")[0].firstChild.data
gconfig["dhcp_defaultRouter"]=elem.getElementsByTagName("defaultRouter")[0].firstChild.data
gconfig["dhcp_defaultDNS"]=elem.getElementsByTagName("defaultDNS")[0].firstChild.data
qconfig=tree.getElementsByTagName("query")
for elem in qconfig:
gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data
for num in range(int(gconfig["offer_count"])):
gconfig["offer_"+str(num+1)]=elem.getElementsByTagName("offer_"+str(num+1))[0].firstChild.data
gconfig["history_sql"]=elem.getElementsByTagName("history_sql")[0].firstChild.data
options=tree.getElementsByTagName("options")
for elem in options:
node=elem.getElementsByTagName("option")
for options in node:
optionsMod.append(options.firstChild.data)Multihilo
Curiosamente, la multihilo en Python se implementa de manera muy comprensible y sencilla.
def PacketWork(data, addr):
...
# implementación del análisis del paquete recibido y la respuesta a él
...
while True:
data, addr = udp_socket.recvfrom(1024) # esperando paquete UDP
thread = threading.Thread(target=PacketWork, args=(data, addr,)).start() # tan pronto como llega, iniciamos en segundo plano la función PacketWork previamente definida con los parámetros
while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
time.sleep(1) # si el número de hilos en ejecución ya supera lo configurado, esperamos a que sea menorRecepción/envío de paquetes DHCP
Para interceptar paquetes UDP que pasan a través de la tarjeta de red, es necesario "levantar" el socket:
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"], 67))
, donde los flags:
- AF_INET — significa que el formato de la dirección será IP: puerto. También puede ser AF_UNIX — donde la dirección se define por el nombre del archivo.
- SOCK_DGRAM significa que estamos recibiendo no un «paquete crudo», sino uno que ya ha pasado por el firewall, y con el paquete parcialmente recortado. Es decir, obtenemos solo el paquete UDP sin el componente «físico» que envuelve el paquete UDP. Si se usa la bandera SOCK_RAW, será necesario analizar también esta «envoltura».
El envío de paquetes puede ser tanto por broadcast:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # cambiamos el socket al modo de envío por broadcast
rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68)), como a la dirección «de donde vino el paquete»:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # cambiamos el socket al modo "muchos oyentes"
rz=udp_socket.sendto(packetack, addr), donde SOL_SOCKET significa «nivel de protocolo» para establecer opciones,
, SO_BROADCAST es la opción que indica que el paquete se envía «por broadcast»
, SO_REUSEADDR es la opción que cambia el socket al modo «muchos oyentes». En teoría, no es necesaria en este caso, pero en uno de los servidores FreeBSD en el que probé, sin esta opción el código no funcionaba.
Análisis del paquete DHCP
Aquí es donde realmente me gustó Python. Resulta que «out of the box» permite manejar el bytecode de forma bastante flexible. Permitiendo convertirlo fácilmente a valores decimales, cadenas y hexadecimales, es decir, lo que realmente necesitamos para entender la estructura del paquete. Así, por ejemplo, se puede obtener un rango de bytes en HEX y en bytes simples:
res["xidhex"]=data[4:8].hex()
res["xidbyte"]=data[4:8], empaquetar los bytes en una estructura:
res["flags"]=pack('BB', data[10], data[11])Obtener IP de la estructura:
res["ciaddr"]=socket.inet_ntoa(pack('BBBB', data[12], data[13], data[14], data[15]));Y al revés:
res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])
Eso es todo 😉
Fuente: habr.com
