
L'obiettivo di questo progetto era:
- Studio del protocollo DHCP nel funzionamento di una rete IPv4
- Studio di Python (un po' più di zero 😉 )
- sostituzione del server (il mio fork), originale , che diventa sempre più difficile da compilare per il nuovo sistema operativo. E non mi piace che il binario non possa essere "cambiato immediatamente".
- ottenere un server DHCP funzionante con la possibilità di selezionare l'indirizzo IP del cliente in base all'indirizzo MAC del cliente o alla combinazione MAC dello switch + porta (Opzione 82)
- scrivere un altro progetto (Oh! è il mio passatempo preferito)
- ricevere critiche sulla mia goffaggine su Habrhabr (meglio se un invito) 😉
Risultato: funziona 😉 Sperimentato su FreeBSD e Ubuntu. Teoricamente, il codice può essere adattato a qualsiasi sistema operativo, poiché non ci sono vincoli specifici nel codice.
Attenzione! Proseguendo ce n'è molto.
Link al repository per gli appassionati .
Il processo di installazione, configurazione e utilizzo del risultato "studio della teoria" è molto più in basso, e successivamente un po' di teoria sul protocollo DHCP. Per me. E per la storia 😉
Un po' di teoria
Cosa è DHCP
È un protocollo di rete che consente a un dispositivo di conoscere il proprio indirizzo IP (e altri parametri come il gateway, DNS e altro) da un server DHCP. Lo scambio di pacchetti avviene tramite il protocollo UDP. Il principio generale del funzionamento del dispositivo quando richiede i parametri di rete è il seguente:
- Il dispositivo (cliente) invia una richiesta UDP di broadcast (DHCPDISCOVER) attraverso la rete chiedendo “qualcuno, per favore dammi un indirizzo IP”. Di solito (ma non sempre) la richiesta proviene dalla porta 68 (sorgente) e viene inviata alla porta 67 (destinazione). Alcuni dispositivi inviano pacchetti anche dalla porta 67. Nel pacchetto DHCPDISCOVER è incluso l'indirizzo MAC del dispositivo cliente.
- Tutti i server DHCP presenti nella rete (ce ne possono essere diversi) generano per il dispositivo che ha inviato il DHCPDISCOVER una proposta DHCPOFFER con le impostazioni di rete, e la inviano anch'essa in broadcast attraverso la rete. L'identificazione di a chi è destinato questo pacchetto avviene tramite l'indirizzo MAC del cliente fornito precedentemente nella richiesta DHCPDISCOVER.
- Il cliente riceve i pacchetti con le proposte delle impostazioni di rete, sceglie quella più attraente (i criteri possono essere vari, ad esempio anche in base al tempo di consegna del pacchetto, al numero di percorsi intermedi) e invia al server DHCP scelto una richiesta ufficiale DHCPREQUEST con le impostazioni di rete. In questo caso, il pacchetto è indirizzato a un server DHCP specifico.
- Il server che riceve il DHCPREQUEST invia un pacchetto in formato DHCPACK, nel quale elenca nuovamente le impostazioni di rete destinate a quel cliente.

Inoltre, esistono pacchetti DHCPINFORM, che vengono inviati dal cliente e il cui scopo è informare il server DHCP che "il cliente è attivo" e sta utilizzando le impostazioni di rete fornite. Nella realizzazione di questo server, tali pacchetti vengono ignorati.
Formato dei pacchetti
In generale, il frame del pacchetto Ethernet appare più o meno così:
![]()
Nel nostro caso considereremo solo i dati del contenuto del pacchetto UDP, senza le intestazioni dei protocolli dei livelli OSI, ossia la struttura DHCP:
DHCPDISCOVER
Quindi, il processo di ottenimento dell'indirizzo IP per un dispositivo inizia quando il cliente DHCP invia una richiesta di broadcast dalla porta 68 a 255.255.255.255:67. Nel questo pacchetto il cliente include il proprio indirizzo MAC e anche ciò che vuole ricevere dal server DHCP. La struttura del pacchetto è descritta nella tabella sottostante.
Tabella della struttura del pacchetto DHCPDISCOVER
Posizione nel pacchetto
Nome del valore
Esempio
Vista
Byte
Spiegazione
1
Richiesta di avvio
1
Esadecimale
1
Tipo di messaggio. 1 — richiesta dal cliente al server, 2 — risposta dal server al cliente
2
Tipo hardware
1
Esadecimale
1
Tipo di indirizzo hardware, in questo protocollo 1 — MAC
3
Lunghezza dell'indirizzo hardware
6
Esadecimale
1
Lunghezza dell'indirizzo MAC del dispositivo
4
Hops
1
Esadecimale
1
Numero di percorsi intermedi
5
ID transazione
23:cf:de:1d
Esadecimale
4
Identificatore unico della transazione. Generato dal cliente all'inizio del processo di richiesta
7
Secondi trascorsi
0
Esadecimale
4
Tempo in secondi dall'inizio del processo di acquisizione dell'indirizzo
9
Flag Bootp
0
Esadecimale
2
Alcuni flag che possono essere impostati come indicazione dei parametri del protocollo
11
Indirizzo IP del cliente
0.0.0.0
Stringa
4
Indirizzo IP del cliente (se presente)
15
Il tuo indirizzo IP del cliente
0.0.0.0
Stringa
4
Indirizzo IP proposto dal server (se presente)
19
Indirizzo IP del server successivo
0.0.0.0
Stringa
4
Indirizzo IP del server (se noto)
23
Indirizzo IP dell'agente di relè
172.16.114.41
Stringa
4
Indirizzo IP dell'agente di relè (ad esempio, switch)
27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente)
31
Padding per l'indirizzo hardware del cliente
Esadecimale
10
Spazio riservato. Di solito riempito con zeri
41
Nome host del server
Stringa
64
Nome del server DHCP. Di solito non viene trasmesso
105
Nome del file di avvio
Stringa
128
Nome del file sul server utilizzato dalle stazioni senza disco durante l'avvio
235
Magic cookie
63:82:53:63
Esadecimale
4
Numero "magico", che consente di identificare che questo pacchetto appartiene al protocollo DHCP
Opzioni DHCP. Possono essere fornite in qualsiasi ordine
236
Numero dell'opzione
53
Dec
1
Opzione 53, che definisce il tipo di pacchetto DHCP
1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM
Lunghezza dell'opzione
1
Dec
1
Valore dell'opzione
1
Dec
1
Numero dell'opzione
50
Dec
1
Quale indirizzo IP desidera ricevere il cliente
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
172.16.134.61
Stringa
4
Numero dell'opzione
55
1
Parametri di rete richiesti dal cliente. La composizione può essere variabile
01 — Maschera di rete
03 — Gateway
06 — DNS
oc — Nome host
0f — nome dominio di rete
1c — indirizzo di broadcast
42 — nome del server TFTP
79 — Route Statica Senza Classe
Lunghezza dell'opzione
8
1
Valore dell'opzione
01:03:06:0c:0f:1c:42:79
8
Numero dell'opzione
82
Dec
Opzione 82, che trasmette l'indirizzo MAC del dispositivo — ripetitore e alcuni valori aggiuntivi.
Di solito — la porta dello switch su cui si trova il client finale DHCP. In questa opzione sono "incapsulati" parametri aggiuntivi. Il primo byte — numero della "sotto-opzione", il secondo la sua lunghezza, quindi il suo valore.
In questo caso, nell'opzione 82, sono incapsulate sotto-opzioni:
Agent Circuit ID = 00:04:00:01:00:04, dove gli ultimi due byte sono la porta del client DHCP da cui è arrivata la richiesta
Agent Remote ID = 00:06:c8:be:19:93:11:48 — indirizzo MAC del dispositivo ripetitore DHCP
Lunghezza dell'opzione
18
Dec
Valore dell'opzione
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Esadecimale
Fine del pacchetto
255
Dec
1
255 simboleggia la fine del pacchetto
DHCPOFFER
Una volta che il server riceve il pacchetto DHCPDISCOVER e se vede di poter offrire qualcosa al cliente, forma una risposta — DHCPDISCOVER. La risposta viene inviata alla porta "da cui è arrivata", in broadcast, poiché in quel momento il cliente non ha ancora un indirizzo IP, quindi può accettare il pacchetto solo se inviato in modo generico. Il cliente riconosce che questo pacchetto è per lui dal proprio indirizzo MAC all'interno del pacchetto, così come dal numero di transazione che genera al momento della creazione del primo pacchetto.
Tabella della struttura del pacchetto DHCPOFFER
Posizione nel pacchetto
Nome del valore (comunemente accettato)
Esempio
Vista
Byte
Spiegazione
1
Richiesta di avvio
1
Esadecimale
1
Tipo di messaggio. 1 — richiesta dal cliente al server, 2 — risposta dal server al cliente
2
Tipo hardware
1
Esadecimale
1
Tipo di indirizzo hardware, in questo protocollo 1 — MAC
3
Lunghezza dell'indirizzo hardware
6
Esadecimale
1
Lunghezza dell'indirizzo MAC del dispositivo
4
Hops
1
Esadecimale
1
Numero di percorsi intermedi
5
ID transazione
23:cf:de:1d
Esadecimale
4
Identificatore unico della transazione. Generato dal cliente all'inizio del processo di richiesta
7
Secondi trascorsi
0
Esadecimale
4
Tempo in secondi dall'inizio del processo di acquisizione dell'indirizzo
9
Flag Bootp
0
Esadecimale
2
Alcuni flag che possono essere impostati come indicazione dei parametri del protocollo. In questo caso, 0 — indica il tipo di richiesta Unicast
11
Indirizzo IP del cliente
0.0.0.0
Stringa
4
Indirizzo IP del cliente (se presente)
15
Il tuo indirizzo IP del cliente
172.16.134.61
Stringa
4
Indirizzo IP proposto dal server (se presente)
19
Indirizzo IP del server successivo
0.0.0.0
Stringa
4
Indirizzo IP del server (se noto)
23
Indirizzo IP dell'agente di relè
172.16.114.41
Stringa
4
Indirizzo IP dell'agente di relè (ad esempio, switch)
27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente)
31
Padding per l'indirizzo hardware del cliente
Esadecimale
10
Spazio riservato. Di solito riempito con zeri
41
Nome host del server
Stringa
64
Nome del server DHCP. Di solito non viene trasmesso
105
Nome del file di avvio
Stringa
128
Nome del file sul server utilizzato dalle stazioni senza disco durante l'avvio
235
Magic cookie
63:82:53:63
Esadecimale
4
Numero "magico", che consente di identificare che questo pacchetto appartiene al protocollo DHCP
Opzioni DHCP. Possono essere fornite in qualsiasi ordine
236
Numero dell'opzione
53
Dec
1
Opzione 53, che determina il tipo di pacchetto DHCP 2 — DHCPOFFER
Lunghezza dell'opzione
1
Dec
1
Valore dell'opzione
2
Dec
1
Numero dell'opzione
1
Dec
1
Opzione che offre al client DHCP la maschera di rete
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
255.255.224.0
Stringa
4
Numero dell'opzione
3
Dec
1
Opzione che offre al client DHCP il gateway predefinito
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
172.16.12.1
Stringa
4
Numero dell'opzione
6
Dec
1
Opzione che offre al client DHCP il DNS
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
8.8.8.8
Stringa
4
Numero dell'opzione
51
Dec
1
Tempo di vita dei parametri di rete assegnati in secondi, dopo il quale il client DHCP deve richiederli nuovamente
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
86400
Dec
4
Numero dell'opzione
82
Dec
1
Opzione 82, ripete ciò che è arrivato nel DHCPDISCOVER
Lunghezza dell'opzione
18
Dec
1
Valore dell'opzione
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Fine del pacchetto
255
Dec
1
255 simboleggia la fine del pacchetto
DHCPREQUEST
Dopo aver ricevuto il DHCPOFFER, il client forma un pacchetto con la richiesta dei parametri di rete non a tutti i server DHCP in rete, ma solo a uno specifico, il cui'offerta DHCPOFFER gli è piaciuta di più. I criteri di "piacere" possono essere vari e dipendono dall'implementazione del client DHCP. Il destinatario della richiesta è indicato tramite l'indirizzo MAC del server DHCP. Inoltre, il pacchetto DHCPREQUEST può essere inviato dal cliente anche senza aver precedentemente generato un DHCPDISCOVER, se un indirizzo IP dal server era già stato ottenuto in precedenza.
Tabella della struttura del pacchetto DHCPREQUEST
Posizione nel pacchetto
Nome del valore (comunemente accettato)
Esempio
Vista
Byte
Spiegazione
1
Richiesta di avvio
1
Esadecimale
1
Tipo di messaggio. 1 — richiesta dal cliente al server, 2 — risposta dal server al cliente
2
Tipo hardware
1
Esadecimale
1
Tipo di indirizzo hardware, in questo protocollo 1 — MAC
3
Lunghezza dell'indirizzo hardware
6
Esadecimale
1
Lunghezza dell'indirizzo MAC del dispositivo
4
Hops
1
Esadecimale
1
Numero di percorsi intermedi
5
ID transazione
23:cf:de:1d
Esadecimale
4
Identificatore unico della transazione. Generato dal cliente all'inizio del processo di richiesta
7
Secondi trascorsi
0
Esadecimale
4
Tempo in secondi dall'inizio del processo di acquisizione dell'indirizzo
9
Flag Bootp
8000
Esadecimale
2
Alcuni flag che possono essere impostati come indicazione dei parametri del protocollo. In questo caso, è impostato su "broadcast"
11
Indirizzo IP del cliente
0.0.0.0
Stringa
4
Indirizzo IP del cliente (se presente)
15
Il tuo indirizzo IP del cliente
172.16.134.61
Stringa
4
Indirizzo IP proposto dal server (se presente)
19
Indirizzo IP del server successivo
0.0.0.0
Stringa
4
Indirizzo IP del server (se noto)
23
Indirizzo IP dell'agente di relè
172.16.114.41
Stringa
4
Indirizzo IP dell'agente di relè (ad esempio, switch)
27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente)
31
Padding per l'indirizzo hardware del cliente
Esadecimale
10
Spazio riservato. Di solito riempito con zeri
41
Nome host del server
Stringa
64
Nome del server DHCP. Di solito non viene trasmesso
105
Nome del file di avvio
Stringa
128
Nome del file sul server utilizzato dalle stazioni senza disco durante l'avvio
235
Magic cookie
63:82:53:63
Esadecimale
4
Numero "magico", che consente di identificare che questo pacchetto appartiene al protocollo DHCP
Opzioni DHCP. Possono essere fornite in qualsiasi ordine
236
Numero dell'opzione
53
Dec
3
Opzione 53, che determina il tipo di pacchetto DHCP 3 — DHCPREQUEST
Lunghezza dell'opzione
1
Dec
1
Valore dell'opzione
3
Dec
1
Numero dell'opzione
61
Dec
1
Identificatore del cliente: 01 (per Ethernet) + indirizzo MAC del cliente
Lunghezza dell'opzione
7
Dec
1
Valore dell'opzione
01:2c:ab:25:ff:72:a6
Esadecimale
7
Numero dell'opzione
60
Dec
"Vendor class identifier". Nel mio caso comunica la versione del client DHCP. È possibile che altri dispositivi restituiscano qualcosa di diverso. Windows, ad esempio, comunica MSFT 5.0
Lunghezza dell'opzione
11
Dec
Valore dell'opzione
udhcp 0.9.8
Stringa
Numero dell'opzione
55
1
Parametri di rete richiesti dal cliente. La composizione può essere variabile
01 — Maschera di rete
03 — Gateway
06 — DNS
oc — Nome host
0f — nome dominio di rete
1c — indirizzo di broadcast
42 — nome del server TFTP
79 — Route Statica Senza Classe
Lunghezza dell'opzione
8
1
Valore dell'opzione
01:03:06:0c:0f:1c:42:79
8
Numero dell'opzione
82
Dec
1
Opzione 82, ripete ciò che è arrivato nel DHCPDISCOVER
Lunghezza dell'opzione
18
Dec
1
Valore dell'opzione
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Fine del pacchetto
255
Dec
1
255 simboleggia la fine del pacchetto
DHCPACK
Come conferma che "sì, esattamente, questo è il tuo indirizzo IP, e non lo darò a nessun altro" dal server DHCP, viene inviato un pacchetto in formato DHCPACK al client. Come gli altri pacchetti, è inviato in broadcast. Tuttavia, nel codice del server DHCP implementato in Python che segue, per precauzione, duplico ogni richiesta broadcast inviando un pacchetto a un indirizzo IP specifico del cliente, se già noto. Inoltre, il server DHCP non si preoccupa del fatto che il pacchetto DHCPACK sia arrivato al cliente. Se il cliente non riceve DHCPACK, dopo un certo periodo ripete semplicemente il DHCPREQUEST.
Tabella della struttura del pacchetto DHCPACK
Posizione nel pacchetto
Nome del valore (comunemente accettato)
Esempio
Vista
Byte
Spiegazione
1
Richiesta di avvio
2
Esadecimale
1
Tipo di messaggio. 1 — richiesta dal cliente al server, 2 — risposta dal server al cliente
2
Tipo hardware
1
Esadecimale
1
Tipo di indirizzo hardware, in questo protocollo 1 — MAC
3
Lunghezza dell'indirizzo hardware
6
Esadecimale
1
Lunghezza dell'indirizzo MAC del dispositivo
4
Hops
1
Esadecimale
1
Numero di percorsi intermedi
5
ID transazione
23:cf:de:1d
Esadecimale
4
Identificatore unico della transazione. Generato dal cliente all'inizio del processo di richiesta
7
Secondi trascorsi
0
Esadecimale
4
Tempo in secondi dall'inizio del processo di acquisizione dell'indirizzo
9
Flag Bootp
8000
Esadecimale
2
Alcuni flag che possono essere impostati come indicazione dei parametri del protocollo. In questo caso, è impostato su "broadcast"
11
Indirizzo IP del cliente
0.0.0.0
Stringa
4
Indirizzo IP del cliente (se presente)
15
Il tuo indirizzo IP del cliente
172.16.134.61
Stringa
4
Indirizzo IP proposto dal server (se presente)
19
Indirizzo IP del server successivo
0.0.0.0
Stringa
4
Indirizzo IP del server (se noto)
23
Indirizzo IP dell'agente di relè
172.16.114.41
Stringa
4
Indirizzo IP dell'agente di relè (ad esempio, switch)
27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente)
31
Padding per l'indirizzo hardware del cliente
Esadecimale
10
Spazio riservato. Di solito riempito con zeri
41
Nome host del server
Stringa
64
Nome del server DHCP. Di solito non viene trasmesso
105
Nome del file di avvio
Stringa
128
Nome del file sul server utilizzato dalle stazioni senza disco durante l'avvio
235
Magic cookie
63:82:53:63
Esadecimale
4
Numero "magico", che consente di identificare che questo pacchetto appartiene al protocollo DHCP
Opzioni DHCP. Possono essere fornite in qualsiasi ordine
236
Numero dell'opzione
53
Dec
3
Opzione 53, che determina il tipo di pacchetto DHCP 5 — DHCPACK
Lunghezza dell'opzione
1
Dec
1
Valore dell'opzione
5
Dec
1
Numero dell'opzione
1
Dec
1
Opzione che offre al client DHCP la maschera di rete
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
255.255.224.0
Stringa
4
Numero dell'opzione
3
Dec
1
Opzione che offre al client DHCP il gateway predefinito
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
172.16.12.1
Stringa
4
Numero dell'opzione
6
Dec
1
Opzione che offre al client DHCP il DNS
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
8.8.8.8
Stringa
4
Numero dell'opzione
51
Dec
1
Tempo di vita dei parametri di rete assegnati in secondi, dopo il quale il client DHCP deve richiederli nuovamente
Lunghezza dell'opzione
4
Dec
1
Valore dell'opzione
86400
Dec
4
Numero dell'opzione
82
Dec
1
Opzione 82, ripete ciò che è arrivato nel DHCPDISCOVER
Lunghezza dell'opzione
18
Dec
1
Valore dell'opzione
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Fine del pacchetto
255
Dec
1
255 simboleggia la fine del pacchetto
Installazione
L'installazione consiste effettivamente nell'installare i moduli python necessari per il funzionamento. Si presume che MySQL sia già installato e configurato.
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
Creiamo il database MySQL, carichiamo il dump pydhcp.sql, configuriamo il file di configurazione.
Configurazione
Tutte le impostazioni del server si trovano in un file in formato xml. File di riferimento:
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')Ora parliamo in dettaglio dei tag:
La sezione dhcpserver descrive le impostazioni principali per avviare il server, vale a dire:
- host — quale indirizzo IP ascolta il server sulla porta 67
- broadcast — quale IP è il broadcast per DHCPOFFER e DHCPACK
- DHCPServer — quale IP ha il server DHCP
- LeaseTime — tempo di affitto dell'indirizzo IP assegnato
- ThreadLimit — numero di thread attivi per la gestione dei pacchetti UDP sulla porta 67. Si prevede che aiuti in progetti ad alto carico 😉
- defaultMask, defaultRouter, defaultDNS — ciò che viene offerto all'utente per default, se l'IP è trovato nel database, ma non sono specificati ulteriori parametri
Sezione mysql:
host, username, password, basename — tutto qui parla da sé. La struttura approssimativa del database è disponibile su
Sezione query: qui sono descritti i comandi per ottenere OFFER/ACK:
- offer_count — numero di query che restituiscono risultati di tipo ip, mask, router, dns
- offer_n — stringa di query. Se il ritorno è vuoto, esegue la query successiva offer
- history_sql — query che ad esempio scrive nella "storia delle autorizzazioni" per l'utente
Nelle query possono partecipare qualsiasi variabile dalla sezione options o opzioni dal protocollo DHCP.
Sezione options. Qui diventa più interessante. Possiamo creare variabili che possiamo usare successivamente nella sezione query.
Ad esempio:
option_82_hex:sw_port1:20:22,questa stringa-comando prende l'intera stringa ricevuta nella richiesta DHCP dell'opzione 82, in formato hex, nell'intervallo da 20 a 22 byte inclusi e la memorizza in una nuova variabile sw_port1 (porta dello switch da cui è arrivata la richiesta)
option_82_hex:sw_mac:26:40,definiamo la variabile sw_mac, prelevando l'hex dall'intervallo 26:40
Per vedere tutte le possibili opzioni che possono essere utilizzate nelle query, possiamo avviare il server con il flag -d. Vedremo circa questo log:
--è arrivato il pacchetto DHCPINFORM sulla porta 67, da 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'}Di conseguenza, possiamo racchiudere qualsiasi variabile in {} e questa verrà utilizzata nella query SQL.
Registriamo per la storia che l'indirizzo IP ricevuto dal client è:


Avvio del server
./pydhcpdb.py -d -c config.xml
— d modalità di output sulla console DEBUG
— c file di configurazione
Analisi post-azione
E ora parliamo in dettaglio dell'implementazione del server in Python. È complicato. Python è stato studiato "al volo". Molti aspetti sono stati realizzati in uno stile: "wow, in qualche modo ho fatto qualcosa che funziona". Non sono affatto ottimizzati, e sono rimasti in questo stato principalmente a causa della scarsa esperienza di sviluppo in Python. Mi soffermerò sui dettagli più interessanti dell'implementazione del server nel "codice".
Parser del file di configurazione XML
Si utilizza il modulo standard Python xml.dom. Sembra semplice, ma durante l'implementazione è mancata notevolmente una documentazione valida e esempi online sull'uso di questo modulo.
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)Multithreading
Stranamente, la multithreading in Python è implementato in modo molto chiaro e semplice.
def PacketWork(data,addr):
...
# implementazione dell'analisi del pacchetto ricevuto e la risposta
...
while True:
data, addr = udp_socket.recvfrom(1024) # aspettiamo un pacchetto UDP
thread = threading.Thread(target=PacketWork, args=(data,addr,)).start() # quando arriva, avviamo in background la funzione PacketWork con i parametri già definiti
while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
time.sleep(1) # se il numero di thread in esecuzione supera il limite delle impostazioni, aspettiamo finché non scendeRicezione/invio di un pacchetto DHCP
Per intercettare i pacchetti UDP che passano attraverso la scheda di rete, è necessario "attivare" il socket:
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"],67))
, dove i flag:
- AF_INET — indica che il formato dell'indirizzo sarà IP: porta. Può esserci anche AF_UNIX — dove l'indirizzo è definito da un nome di file.
- SOCK_DGRAM — indica che riceviamo non un "pacchetto grezzo", ma un pacchetto già filtrato dal firewall e parzialmente rimosso. Cioè riceviamo solo il pacchetto UDP senza la parte "fisica" del pacchetto UDP. Se si utilizza il flag SOCK_RAW, sarà necessario analizzare anche questa "involucro".
L'invio del pacchetto può avvenire sia tramite broadcast:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # commutiamo il socket in modalità invio broadcast
rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68)), sia all'indirizzo "da cui è arrivato il pacchetto":
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # commutiamo il socket in modalità "multi ascoltatori"
rz=udp_socket.sendto(packetack, addr), dove SOL_SOCKET significa "livello protocollo" per impostare le opzioni,
, SO_BROADCAST opzione per inviare il pacchetto "in broadcast"
, SO_REUSEADDR opzione per mettere il socket in modalità "multi ascoltatori". In teoria non è necessaria in questo caso, ma su uno dei server FreeBSD che ho testato, senza questa opzione il codice non funzionava.
Analisi del pacchetto DHCP
Qui ho veramente apprezzato Python. Risulta che dalla "scatola" permette di trattare abbastanza liberamente il bytecode. Consentendo di convertire molto facilmente in valori decimali, stringhe e esadecimale — cioè ciò che ci serve per comprendere la struttura del pacchetto. Ad esempio, posso ottenere l'intervallo di byte in HEX e in byte semplici:
res["xidhex"]=data[4:8].hex()
res["xidbyte"]=data[4:8], impacchettare i byte in una struttura:
res["flags"]=pack('BB',data[10],data[11])Ottenere l'IP dalla struttura:
res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));E viceversa:
res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])
Questo è tutto 😉
Fonte: habr.com
