DHCP+Mysql server in Python

DHCP+Mysql server in Python

L'obiettivo di questo progetto era:

  • Studio del protocollo DHCP in funzionamento su rete IPv4
  • Studio di Python (un po' più di zero 😉 )
  • sostituzione del server DB2DHCP (il mio fork), originale qui, che diventa sempre più difficile raccogliere per il nuovo sistema operativo. E non mi piace che il binario non possa essere "cambiato subito"
  • ottenere un server DHCP funzionante con la possibilità di ottenere l'indirizzo IP dell'abbonato per MAC dell'abbonato o per la combinazione MAC switch + porta (Opzione 82)
  • scrivere un altro progetto (Oh! è la mia attività preferita)
  • ottenere rimproveri per la mia goffaggine su HabrHabr (meglio un invito) 😉

Risultato: funziona 😉 Provato su sistemi operativi FreeBSD e Ubuntu. Teoricamente, il codice può essere fatto funzionare su qualsiasi sistema operativo, poiché non sembra avere legami specifici nel codice.
Attenzione! Avanti ci sarà molto.

Link al repository per i curiosi «toccare dal vivo».

Il processo di installazione, configurazione e utilizzo del risultato dello "studio dei materiali" è molto più in basso, e di seguito un po' di teoria sul protocollo DHCP. Per me. E per la storia 😉

Un po' di teoria

Che cos'è DHCP

È un protocollo di rete che consente a un dispositivo di scoprire il proprio indirizzo IP (e altri parametri come gateway, DNS e altro) da un server DHCP. Lo scambio di pacchetti avviene tramite il protocollo UDP. Il principio generale di funzionamento del dispositivo durante la richiesta dei parametri di rete è il seguente:

  1. Il dispositivo (cliente) invia una richiesta broadcast UDP (DHCPDISCOVER) in tutta la rete con la richiesta "qualcuno, datemi un indirizzo IP". Di solito (ma non sempre) la richiesta avviene dalla porta 68 (sorgente), mentre la destinazione è la porta 67 (destinazione). Alcuni dispositivi inviano pacchetti anche dalla porta 67. All'interno del pacchetto DHCPDISCOVER è incluso l'indirizzo MAC del dispositivo cliente.
  2. Tutti i server DHCP presenti nella rete (e ce ne possono essere diversi) formano un'offerta DHCPOFFER con le impostazioni di rete per il dispositivo che ha inviato il DHCPDISCOVER e la inviano anche in broadcasting attraverso la rete. L'identificazione di a chi è destinato questo pacchetto avviene attraverso l'indirizzo MAC del cliente, fornito in precedenza nella richiesta DHCPDISCOVER.
  3. Il cliente riceve pacchetti con offerte di configurazione di rete, sceglie quello più attraente (i criteri possono essere vari, ad esempio anche in base ai tempi 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.
  4. Il server che riceve il DHCPREQUEST inoltra un pacchetto in formato DHCPACK, nel quale elenca nuovamente le impostazioni di rete destinate a questo cliente.

DHCP+Mysql server in Python

Inoltre, ci sono pacchetti DHCPINFORM che vengono inviati dal cliente, il cui scopo è informare il server DHCP che il "cliente è attivo" e sta utilizzando le impostazioni di rete fornite. Nel caso di questo server, questi pacchetti vengono ignorati.

Formato dei pacchetti

In generale, il frame del pacchetto Ethernet appare più o meno così:

DHCP+Mysql server in Python

In questo caso, considereremo solo i dati contenuti nel pacchetto UDP, senza gli header dei protocolli dei livelli OSI, precisamente la struttura DHCP:

DHCPDISCOVER

Quindi, il processo per ottenere un indirizzo IP per il dispositivo inizia con il cliente DHCP che invia una richiesta broadcast dalla porta 68 a 255.255.255.255:67. In questo pacchetto, il cliente include il proprio indirizzo MAC e specifica cosa desidera ricevere dal server DHCP. La struttura del pacchetto è descritta nella tabella qui sotto.

Tabella della struttura del pacchetto DHCPDISCOVER

Posizione nel pacchetto
Nome del valore
Esempio
Rappresentazione
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
Salti
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 dell'operazione 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 indicazioni dei parametri del protocollo.

11
Indirizzo IP del cliente
0.0.0.0
Riga
4
Indirizzo IP del cliente (se presente)

15
Il tuo indirizzo IP del cliente
0.0.0.0
Riga
4
Indirizzo IP proposto dal server (se presente)

19
Indirizzo IP del server successivo
0.0.0.0
Riga
4
Indirizzo IP del server (se noto)

23
Indirizzo IP dell'agente di relay
172.16.114.41
Riga
4
Indirizzo IP dell'agente di relay (ad esempio uno switch).

27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente).

31
Padding dell'indirizzo hardware del cliente
 
Esadecimale
10
Spazio riservato. Di solito riempito di zeri.

41
Nome host del server
 
Riga
64
Nome del server DHCP. Di solito non viene trasmesso.

105
Nome del file di avvio
 
Riga
128
Nome del file sul server, usato dalle stazioni senza disco durante il caricamento

235
Magic cookie
63:82:53:63
Esadecimale
4
Il numero «magico» attraverso il quale è possibile determinare, tra l'altro, che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Possono essere 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 client

 
Lunghezza dell'opzione
4
Dec
1

 
Valore dell'opzione
172.16.134.61
Riga
4

 
Numero dell'opzione
55
 
1
Parametri di rete richiesti dal client. La composizione può variare

01 — Maschera di rete
03 — Gateway
06 — DNS
oc — Nome host
0f — Nome di dominio di rete
1c — Indirizzo di richiesta di trasmissione (broadcast)
42 — Nome del server TFTP
79 — Rotta 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 — il ripetitore e alcuni valori aggiuntivi.

Di solito — il porto dello switch su cui opera il client finale DHCP. In questa opzione sono «incapsulati» parametri aggiuntivi. Il primo byte è il numero della «sottopzione», il secondo è la sua lunghezza, seguito dal suo valore.

In questo caso, nella opzione 82, sono incapsulate le sottopzioni:
Agent Circuit ID = 00:04:00:01:00:04, dove gli ultimi due byte rappresentano il porto 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

Non appena il server riceve il pacchetto DHCPDISCOVER e vede che può offrire qualcosa al client richiesto, formula una risposta — DHCPDISCOVER. La risposta viene inviata al porto «da cui è arrivata», in broadcast, poiché in quel momento il client non ha ancora un indirizzo IP, quindi il pacchetto può essere ricevuto solo se inviato in broadcast. Il client riconosce che si tratta di un pacchetto per lui grazie al suo indirizzo MAC all'interno del pacchetto, così come al numero di transazione che genera al momento della creazione del primo pacchetto.

Tabella della struttura del pacchetto DHCPOFFER

Posizione nel pacchetto
Nome del valore (comune)
Esempio
Rappresentazione
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
Salti
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 dell'operazione 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
Riga
4
Indirizzo IP del cliente (se presente)

15
Il tuo indirizzo IP del cliente
172.16.134.61
Riga
4
Indirizzo IP proposto dal server (se presente)

19
Indirizzo IP del server successivo
0.0.0.0
Riga
4
Indirizzo IP del server (se noto)

23
Indirizzo IP dell'agente di relay
172.16.114.41
Riga
4
Indirizzo IP dell'agente di relay (ad esempio uno switch).

27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente).

31
Padding dell'indirizzo hardware del cliente
 
Esadecimale
10
Spazio riservato. Di solito riempito di zeri.

41
Nome host del server
 
Riga
64
Nome del server DHCP. Di solito non viene trasmesso.

105
Nome del file di avvio
 
Riga
128
Nome del file sul server, usato dalle stazioni senza disco durante il caricamento

235
Magic cookie
63:82:53:63
Esadecimale
4
Il numero «magico» attraverso il quale è possibile determinare, tra l'altro, che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Possono essere in qualsiasi ordine

236
Numero dell'opzione
53
Dec
1
Opzione 53, che definisce 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
Riga
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
Riga
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
Riga
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 di nuovo

 
Lunghezza dell'opzione
4
Dec
1

 
Valore dell'opzione
86400
Dec
4

 
Numero dell'opzione
82
Dec
1
Opzione 82, ripete ciò che è stato ricevuto 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 che il client riceve il DHCPOFFER, genera un pacchetto con una richiesta di parametri di rete non a tutti i server DHCP nella rete, ma solo a uno specifico, il cui DHCPOFFER gli è piaciuto di più. I criteri di "piaciuto" possono essere vari e dipendono dall'implementazione del client DHCP. Il destinatario della richiesta è specificato mediante l'indirizzo MAC del server DHCP. Inoltre, il pacchetto DHCPREQUEST può essere inviato dal client anche senza aver prima generato il DHCPDISCOVER, se l'indirizzo IP del server era già stato ottenuto in precedenza.

Tabella della struttura del pacchetto DHCPREQUEST

Posizione nel pacchetto
Nome del valore (comune)
Esempio
Rappresentazione
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
Salti
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 dell'operazione 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 è stato impostato "broadcast"

11
Indirizzo IP del cliente
0.0.0.0
Riga
4
Indirizzo IP del cliente (se presente)

15
Il tuo indirizzo IP del cliente
172.16.134.61
Riga
4
Indirizzo IP proposto dal server (se presente)

19
Indirizzo IP del server successivo
0.0.0.0
Riga
4
Indirizzo IP del server (se noto)

23
Indirizzo IP dell'agente di relay
172.16.114.41
Riga
4
Indirizzo IP dell'agente di relay (ad esempio uno switch).

27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente).

31
Padding dell'indirizzo hardware del cliente
 
Esadecimale
10
Spazio riservato. Di solito riempito di zeri.

41
Nome host del server
 
Riga
64
Nome del server DHCP. Di solito non viene trasmesso.

105
Nome del file di avvio
 
Riga
128
Nome del file sul server, usato dalle stazioni senza disco durante il caricamento

235
Magic cookie
63:82:53:63
Esadecimale
4
Il numero «magico» attraverso il quale è possibile determinare, tra l'altro, che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Possono essere in qualsiasi ordine

236
Numero dell'opzione
53
Dec
3
Opzione 53, che definisce 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 client: 01 (per Ethernet) + indirizzo MAC del client

 
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, riporta MSFT 5.0

 
Lunghezza dell'opzione
11
Dec
 

 
Valore dell'opzione
udhcp 0.9.8
Riga
 

 
Numero dell'opzione
55
 
1
Parametri di rete richiesti dal client. La composizione può variare

01 — Maschera di rete
03 — Gateway
06 — DNS
oc — Nome host
0f — Nome di dominio di rete
1c — Indirizzo di richiesta di trasmissione (broadcast)
42 — Nome del server TFTP
79 — Rotta 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 è stato ricevuto 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" da parte del server DHCP, viene inviata un pacchetto in formato DHCPACK dal server al client. Anche questo, come gli altri pacchetti, viene inviato in broadcast. Tuttavia, nel codice sottostante del server DHCP implementato in Python, per sicurezza duplico qualsiasi richiesta in broadcast, inviando un pacchetto a un IP specifico del client, se già noto. Inoltre, il server DHCP non è affatto preoccupato se il pacchetto DHCPACK è arrivato al client. Se il client non riceve il DHCPACK, dopo un certo tempo ripete semplicemente il DHCPREQUEST.

Tabella della struttura del pacchetto DHCPACK

Posizione nel pacchetto
Nome del valore (comune)
Esempio
Rappresentazione
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
Salti
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 dell'operazione 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 è stato impostato "broadcast"

11
Indirizzo IP del cliente
0.0.0.0
Riga
4
Indirizzo IP del cliente (se presente)

15
Il tuo indirizzo IP del cliente
172.16.134.61
Riga
4
Indirizzo IP proposto dal server (se presente)

19
Indirizzo IP del server successivo
0.0.0.0
Riga
4
Indirizzo IP del server (se noto)

23
Indirizzo IP dell'agente di relay
172.16.114.41
Riga
4
Indirizzo IP dell'agente di relay (ad esempio uno switch).

27
Indirizzo MAC del cliente
14:d6:4d:a7:c9:55
Esadecimale
6
Indirizzo MAC del mittente del pacchetto (cliente).

31
Padding dell'indirizzo hardware del cliente
 
Esadecimale
10
Spazio riservato. Di solito riempito di zeri.

41
Nome host del server
 
Riga
64
Nome del server DHCP. Di solito non viene trasmesso.

105
Nome del file di avvio
 
Riga
128
Nome del file sul server, usato dalle stazioni senza disco durante il caricamento

235
Magic cookie
63:82:53:63
Esadecimale
4
Il numero «magico» attraverso il quale è possibile determinare, tra l'altro, che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Possono essere in qualsiasi ordine

236
Numero dell'opzione
53
Dec
3
Opzione 53, che definisce 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
Riga
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
Riga
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
Riga
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 di nuovo

 
Lunghezza dell'opzione
4
Dec
1

 
Valore dell'opzione
86400
Dec
4

 
Numero dell'opzione
82
Dec
1
Opzione 82, ripete ciò che è stato ricevuto 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 fondamentalmente nell'installazione dei 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 e configuriamo il file di configurazione.

Configurazione

Tutte le impostazioni del server sono contenute in un file di 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 più nel dettaglio sui tag:

La sezione dhcpserver descrive le impostazioni principali per l'avvio del server, ossia:

  • host — quale indirizzo IP ascolta il server sulla porta 67
  • broadcast — quale IP è utilizzato come broadcast per DHCPOFFER e DHCPACK
  • DHCPServer — quale IP ha il server DHCP
  • LeaseTime — tempo di noleggio dell'indirizzo IP rilasciato
  • ThreadLimit — quanti thread possono essere eseguiti contemporaneamente per l'elaborazione dei pacchetti UDP in arrivo sulla porta 67. Si presume che possa aiutare in progetti ad alta intensità di carico 😉
  • defaultMask, defaultRouter, defaultDNS — ciò che viene offerto al cliente per impostazione predefinita, se l'IP è trovato nel database, ma non sono specificati parametri aggiuntivi

Sezione mysql:

host, username, password, basename — tutto parla da sé. La struttura approssimativa del database è pubblicata su GitHub

Sezione query: qui sono descritti i requisiti per ottenere OFFER/ACK:

  • offer_count — numero di righe con requisiti che restituiscono risultati del tipo ip, mask, router, dns
  • offer_n — stringa di richiesta. Se la restituzione è vuota, esegue il successivo requisito offer
  • history_sql — richiesta che scrive ad esempio nella 'storia di autorizzazione' per il cliente

Nelle richieste possono partecipare qualsiasi variabile dalla sezione options o opzioni dal protocollo DHCP.

Sezione options. Qui è già più interessante. Qui possiamo creare variabili che possiamo utilizzare in seguito nella sezione query.

Ad esempio:

option_82_hex:sw_port1:20:22

, questa riga di comando prende l'intera stringa ricevuta nella richiesta DHCP dell'opzione 82, in formato esadecimale, nell'intervallo dai 20 ai 22 byte inclusi e la mette in una nuova variabile sw_port1 (porta dello switch da cui è arrivata la richiesta)

opzione_82_hex:sw_mac:26:40

, definiamo la variabile sw_mac, prendendo l'esadecimale dall'intervallo 26:40

È possibile visualizzare tutte le opzioni che possono essere utilizzate nelle richieste avviando il server con la chiave -d. Vedremo un log simile a questo:

-- è arrivato un 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'}

Pertanto possiamo racchiudere qualsiasi variabile tra {} e sarà utilizzata nella richiesta SQL.

Registriamo per la storia quale indirizzo IP ha ricevuto il cliente:

DHCP+Mysql server in Python

DHCP+Mysql server in Python

Avvio del server

.\/pydhcpdb.py -d -c config.xml

— d modalità di output nella console DEBUG
— c <nome_file> file di configurazione

Analisi dei voli

E ora più in dettaglio sull'implementazione del server in Python. È una sofferenza. Python è stato studiato 'in corsa'. Molti aspetti sono stati fatti nello stile: 'wow, in qualche modo ho fatto funzionare questo'. Non sono affatto ottimizzati e sono stati mantenuti in questo modo principalmente a causa della poca esperienza nello sviluppo in Python. Mi soffermerò sui punti più interessanti dell'implementazione del server nel 'codice'.

Parser del file di configurazione XML

Viene utilizzato il modulo standard Python xml.dom. Sembra semplice, ma nella realizzazione è mancata notevolmente una documentazione adeguata 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 è implementata in modo molto chiaro e semplice.

def PacketWork(data,addr): 
...
# implementazione dell'analisi del pacchetto ricevuto e della risposta a esso
...


while True:
    data, addr = udp_socket.recvfrom(1024) # aspettiamo un pacchetto UDP
    thread = threading.Thread(target=PacketWork, args=(data,addr,)).start()	# appena arriva - avviamo in background una funzione precedentemente definita PacketWork con i parametri
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # se il numero di thread già avviati è maggiore di quello impostato, aspettiamo finché non diminuisce

Ricezione/invio del 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 è specificato con il nome del file.
  • SOCK_DGRAM — significa che riceviamo non un "pacchetto grezzo", ma già passato attraverso il firewall e con il pacchetto parzialmente troncato. Cioè, riceviamo solo il pacchetto UDP senza la "parte fisica" del pacchetto UDP. Se utilizzi il flag SOCK_RAW, sarà necessario analizzare anche questa "parte".

L'invio di un pacchetto può avvenire tramite broadcasting:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) #Passiamo il socket in modalità invio broadcasting
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, oppure all'indirizzo "da cui è arrivato il pacchetto":

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # Passiamo il socket in modalità "molti ascoltatori"
                        rz=udp_socket.sendto(packetack, addr)

, dove SOL_SOCKET indica il "livello del protocollo" per impostare le opzioni,

, SO_BROADCAST è l'opzione che indica che il pacchetto è inviato "in broadcasting"

  , SO_REUSEADDR è l'opzione che cambia il socket alla modalità "molti ascoltatori". Teoricamente, non è necessaria in questo caso, ma su uno dei server FreeBSD su cui ho testato, senza questa opzione il codice non funzionava.

Analisi del pacchetto DHCP

Qui Python mi è piaciuto davvero. Si scopre che "out of the box" consente di trattare il bytecode in modo molto flessibile. Permettendo di convertirlo facilmente in valori decimali, stringhe e hex — cioè ciò di cui abbiamo effettivamente bisogno per comprendere la struttura del pacchetto. Ad esempio, si può ottenere un intervallo di byte in HEX e nei byte semplici:

    res["xidhex"]=data[4:8].hex()
    res["xidbyte"]=data[4:8]

, imballare 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

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster