Server DHCP+Mysql su Python

Server DHCP+Mysql su Python

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 DB2DHCP (il mio fork), originale qui, 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 «toccarlo dal vivo».

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:

  1. 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.
  2. 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.
  3. 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.
  4. Il server che riceve il DHCPREQUEST invia un pacchetto in formato DHCPACK, nel quale elenca nuovamente le impostazioni di rete destinate a quel cliente.

Server DHCP+Mysql su Python

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ì:

Server DHCP+Mysql su Python

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 GitHub

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 è:

Server DHCP+Mysql su Python

Server DHCP+Mysql su Python

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 scende

Ricezione/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

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