Server DHCP+MySQL in Python

Server DHCP+MySQL in Python

Lo scopo di questo progetto era:

  • Conoscere DHCP su una rete IPv4
  • Imparare Python (un po' più che da zero 😉)
  • sostituzione del server DB2DHCP (la mia forchetta), originale qui, che sta diventando sempre più difficile da assemblare per il nuovo sistema operativo. E non mi piace che sia un binario che non c'è modo di "cambiare in questo momento"
  • ottenere un server DHCP funzionante con la possibilità di selezionare l'indirizzo IP di un abbonato utilizzando la combinazione mac dell'abbonato o switch mac+porta (Opzione 82)
  • scrivere un'altra bici (Oh! questa è la mia attività preferita)
  • ricevere commenti sulla tua abilità nel club su Habrahabr (o meglio ancora, un invito) 😉

Risultato: funziona 😉 Testato su FreeBSD e Ubuntu OS. In teoria, è possibile chiedere al codice di funzionare con qualsiasi sistema operativo, perché Sembra che non ci siano vincoli specifici nel codice.
Accuratamente! C'è molto altro in arrivo.

Collegamento al repository per dilettanti "toccare vivo".

Il processo di installazione, configurazione e utilizzo è il risultato dello “studio dell'hardware” molto più breve, e poi un po' di teoria sul protocollo DHCP. Per me. E per la storia 😉

Una piccola teoria

Cos'è il DHCP

Si tratta di un protocollo di rete che consente a un dispositivo di scoprire il proprio indirizzo IP (e altri parametri come gateway, DNS, ecc.) da un server DHCP. I pacchetti vengono scambiati utilizzando il protocollo UDP. Il principio generale di funzionamento del dispositivo quando si richiedono i parametri di rete è il seguente:

  1. Il dispositivo (client) invia una richiesta di trasmissione UDP (DHCPDISCOVER) attraverso la rete con la richiesta "beh, qualcuno mi dia un indirizzo IP". Inoltre, solitamente (ma non sempre) la richiesta avviene dalla porta 68 (sorgente), e la destinazione è la porta 67 (destinazione). Alcuni dispositivi inviano pacchetti anche dalla porta 67. L'indirizzo MAC del dispositivo client è incluso nel pacchetto DHCPDISCOVER.
  2. Tutti i server DHCP presenti sulla rete (e potrebbero essercene diversi) formano un'offerta DHCPOFFER con le impostazioni di rete per il dispositivo che ha inviato DHCPDISCOVER e lo trasmette anche sulla rete. L'identificazione del destinatario di questo pacchetto si basa sull'indirizzo MAC del client fornito in precedenza nella richiesta DHCPDISCOVER.
  3. Il client accetta i pacchetti con proposte di impostazioni di rete, seleziona quello più interessante (i criteri possono essere diversi, ad esempio l'ora di consegna dei pacchetti, il numero di percorsi intermedi) ed effettua una "richiesta ufficiale" DHCPREQUEST con le impostazioni di rete dal server DHCP che preferisce. In questo caso, il pacchetto va a un server DHCP specifico.
  4. Il server che ha ricevuto il DHCPREQUEST invia un pacchetto in formato DHCPACK in cui elenca ancora una volta le impostazioni di rete previste per questo client

Server DHCP+MySQL in Python

Inoltre, ci sono pacchetti DHCPINFORM che provengono dal client e il cui scopo è informare il server DHCP che il "client è vivo" e sta utilizzando le impostazioni di rete fornite. Nell'implementazione di questo server, questi pacchetti vengono ignorati.

Formato del pacchetto

In generale, un frame di pacchetto Ethernet assomiglia a questo:

Server DHCP+MySQL in Python

Nel nostro caso considereremo solo i dati direttamente dal contenuto del pacchetto UDP, senza intestazioni di protocollo del livello OSI, ovvero la struttura DHCP:

DHCPSCOPRI

Pertanto, il processo per ottenere un indirizzo IP per un dispositivo inizia con il client DHCP che invia una richiesta broadcast dalla porta 68 a 255.255.255.255:67. In questo pacchetto il client include il suo indirizzo MAC e cosa esattamente desidera ricevere dal server DHCP. La struttura del pacchetto è descritta nella tabella seguente.

Tabella della struttura dei pacchetti DHCPDISCOVER

Posizione nel pacchetto
Nome del valore
esempio
idea
byte
Chiarificazione

1
Richiesta di avvio
1
Hex
1
Tipo di messaggio. 1 - richiesta dal client al server, 2 - risposta dal server al client

2
Tipo di hardware
1
Hex
1
Tipo di indirizzo hardware, in questo protocollo 1 - MAC

3
Lunghezza degli indirizzi hardware
6
Hex
1
Lunghezza dell'indirizzo MAC del dispositivo

4
Luppolo
1
Hex
1
Numero di tratte intermedie

5
ID transazione
23:cf:de:1d
Hex
4
Identificatore univoco della transazione. Generato dal client all'inizio di un'operazione di richiesta

7
Il secondo è trascorso
0
Hex
4
Tempo in secondi dall'inizio del processo per ottenere un indirizzo

9
Flag di avvio
0
Hex
2
Alcuni flag che possono essere impostati per indicare i parametri del protocollo

11
Indirizzo IP del cliente
0.0.0.0
fila
4
Indirizzo IP del client (se presente)

15
L'indirizzo IP del tuo cliente
0.0.0.0
fila
4
Indirizzo IP offerto dal server (se disponibile)

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

23
Indirizzo IP dell'agente di inoltro
172.16.114.41
fila
4
Indirizzo IP dell'agente di inoltro (ad esempio, uno switch)

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

31
Riempimento dell'indirizzo hardware del client
 
Hex
10
Posto riservato. Di solito riempito con zeri

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

105
Nome del file di avvio
 
fila
128
Nome del file sul server utilizzato dalle stazioni diskless durante l'avvio

235
Biscotto magico
63: 82: 53: 63
Hex
4
Numero "magico", secondo il quale, incl. puoi determinare che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Può andare in qualsiasi ordine

236
Numero dell'opzione
53
Dicembre
1
Opzione 53, che specifica il tipo di pacchetto DHCP

1 - DHCPDISCOVER
3 - RICHIESTA DHCP
2 - OFFERTA DHCP
5-DHACKACK
8 - DHCPINFORM

 
Lunghezza dell'opzione
1
Dicembre
1

 
Valore dell'opzione
1
Dicembre
1

 
Numero dell'opzione
50
Dicembre
1
Quale indirizzo IP vuole ricevere il client?

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
172.16.134.61
fila
4

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

01 — Maschera di rete
03 - Portale
06-DNS
oc: nome host
0f - nome di dominio di rete
1c - indirizzo della richiesta di trasmissione (broadcast)
42 - Nome del server TFTP
79 - Percorso statico senza classi

 
Lunghezza dell'opzione
8
 
1

 
Valore dell'opzione
01:03:06:0c:0f:1c:42:79
 
8

 
Numero dell'opzione
82
Dicembre
 
Opzione 82, che trasmette l'indirizzo MAC del dispositivo ripetitore e alcuni valori aggiuntivi.

Molto spesso si tratta della porta dello switch su cui viene eseguito il client DHCP finale. Questa opzione contiene parametri aggiuntivi. Il primo byte è il numero della "sottoopzione", il secondo è la sua lunghezza, quindi il suo valore.

In questo caso, nell'opzione 82, le sottoopzioni sono annidate:
ID circuito agente = 00:04:00:01:00:04, dove gli ultimi due byte sono la porta del client DHCP da cui proviene la richiesta

ID remoto agente = 00:06:c8:be:19:93:11:48 - Indirizzo MAC del dispositivo ripetitore DHCP

 
Lunghezza dell'opzione
18
Dicembre
 

 
Valore dell'opzione
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Fine del pacchetto
255
Dicembre
1
255 simboleggia la fine del pacchetto

OFFERTA DHCP

Non appena il server riceve il pacchetto DHCPDISCOVER e se vede che può offrire al client qualcosa da quello richiesto, genera una risposta per questo: DHCPDISCOVER. La risposta viene inviata al porto “da dove è arrivata”, via broadcast, perché in questo momento il client non ha ancora un indirizzo IP, quindi può accettare il pacchetto solo se inviato via broadcast. Il cliente riconosce che si tratta di un pacchetto per lui dal suo indirizzo MAC all'interno del pacchetto, nonché dal numero di transazione che genera al momento della creazione del primo pacchetto.

Tabella della struttura dei pacchetti DHCPOFFER

Posizione nel pacchetto
Nome del valore (comune)
esempio
idea
byte
Chiarificazione

1
Richiesta di avvio
1
Hex
1
Tipo di messaggio. 1 - richiesta dal client al server, 2 - risposta dal server al client

2
Tipo di hardware
1
Hex
1
Tipo di indirizzo hardware, in questo protocollo 1 - MAC

3
Lunghezza degli indirizzi hardware
6
Hex
1
Lunghezza dell'indirizzo MAC del dispositivo

4
Luppolo
1
Hex
1
Numero di tratte intermedie

5
ID transazione
23:cf:de:1d
Hex
4
Identificatore univoco della transazione. Generato dal client all'inizio di un'operazione di richiesta

7
Il secondo è trascorso
0
Hex
4
Tempo in secondi dall'inizio del processo per ottenere un indirizzo

9
Flag di avvio
0
Hex
2
Alcuni flag che possono essere impostati per indicare i parametri del protocollo. In questo caso, 0 indica il tipo di richiesta Unicast

11
Indirizzo IP del cliente
0.0.0.0
fila
4
Indirizzo IP del client (se presente)

15
L'indirizzo IP del tuo cliente
172.16.134.61
fila
4
Indirizzo IP offerto dal server (se disponibile)

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

23
Indirizzo IP dell'agente di inoltro
172.16.114.41
fila
4
Indirizzo IP dell'agente di inoltro (ad esempio, uno switch)

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

31
Riempimento dell'indirizzo hardware del client
 
Hex
10
Posto riservato. Di solito riempito con zeri

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

105
Nome del file di avvio
 
fila
128
Nome del file sul server utilizzato dalle stazioni diskless durante l'avvio

235
Biscotto magico
63: 82: 53: 63
Hex
4
Numero "magico", secondo il quale, incl. puoi determinare che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Può andare in qualsiasi ordine

236
Numero dell'opzione
53
Dicembre
1
Opzione 53, che definisce il tipo di pacchetto DHCP 2 - DHCPOFFER

 
Lunghezza dell'opzione
1
Dicembre
1

 
Valore dell'opzione
2
Dicembre
1

 
Numero dell'opzione
1
Dicembre
1
Opzione per offrire al client DHCP una maschera di rete

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
255.255.224.0
fila
4

 
Numero dell'opzione
3
Dicembre
1
Opzione per offrire al client DHCP un gateway predefinito

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
172.16.12.1
fila
4

 
Numero dell'opzione
6
Dicembre
1
Opzione per offrire DHCP al client DNS

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
8.8.8.8
fila
4

 
Numero dell'opzione
51
Dicembre
1
La durata in secondi dei parametri di rete emessi, dopodiché il client DHCP deve richiederli nuovamente

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
86400
Dicembre
4

 
Numero dell'opzione
82
Dicembre
1
L'opzione 82 ripete ciò che è arrivato in DHCPDISCOVER

 
Lunghezza dell'opzione
18
Dicembre
1

 
Valore dell'opzione
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dicembre
18

 
Fine del pacchetto
255
Dicembre
1
255 simboleggia la fine del pacchetto

DHCPRICHIESTA

Dopo che il client ha ricevuto DHCPOFFER, forma un pacchetto richiedendo i parametri di rete non a tutti i server DHCP della rete, ma solo a uno specifico, di cui ha "apprezzato" di più l'offerta DHCPOFFER. I criteri "mi piace" possono essere diversi e dipendono dall'implementazione DHCP del client. Il destinatario della richiesta viene specificato utilizzando l'indirizzo MAC del server DHCP. Inoltre, un pacchetto DHCPREQUEST può essere inviato dal client senza prima generare DHCPDISCOVER, se l’indirizzo IP del server è già stato ottenuto in precedenza.

Tabella della struttura dei pacchetti DHCPREQUEST

Posizione nel pacchetto
Nome del valore (comune)
esempio
idea
byte
Chiarificazione

1
Richiesta di avvio
1
Hex
1
Tipo di messaggio. 1 - richiesta dal client al server, 2 - risposta dal server al client

2
Tipo di hardware
1
Hex
1
Tipo di indirizzo hardware, in questo protocollo 1 - MAC

3
Lunghezza degli indirizzi hardware
6
Hex
1
Lunghezza dell'indirizzo MAC del dispositivo

4
Luppolo
1
Hex
1
Numero di tratte intermedie

5
ID transazione
23:cf:de:1d
Hex
4
Identificatore univoco della transazione. Generato dal client all'inizio di un'operazione di richiesta

7
Il secondo è trascorso
0
Hex
4
Tempo in secondi dall'inizio del processo per ottenere un indirizzo

9
Flag di avvio
8000
Hex
2
Alcuni flag che possono essere impostati per indicare i parametri del protocollo. In questo caso è impostato “broadcast”.

11
Indirizzo IP del cliente
0.0.0.0
fila
4
Indirizzo IP del client (se presente)

15
L'indirizzo IP del tuo cliente
172.16.134.61
fila
4
Indirizzo IP offerto dal server (se disponibile)

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

23
Indirizzo IP dell'agente di inoltro
172.16.114.41
fila
4
Indirizzo IP dell'agente di inoltro (ad esempio, uno switch)

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

31
Riempimento dell'indirizzo hardware del client
 
Hex
10
Posto riservato. Di solito riempito con zeri

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

105
Nome del file di avvio
 
fila
128
Nome del file sul server utilizzato dalle stazioni diskless durante l'avvio

235
Biscotto magico
63: 82: 53: 63
Hex
4
Numero "magico", secondo il quale, incl. puoi determinare che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Può andare in qualsiasi ordine

236
Numero dell'opzione
53
Dicembre
3
Opzione 53, che definisce il tipo di pacchetto DHCP 3 - DHCPREQUEST

 
Lunghezza dell'opzione
1
Dicembre
1

 
Valore dell'opzione
3
Dicembre
1

 
Numero dell'opzione
61
Dicembre
1
ID client: 01 (per Ehernet) + indirizzo MAC client

 
Lunghezza dell'opzione
7
Dicembre
1

 
Valore dell'opzione
01:2c:ab:25:ff:72:a6
Hex
7

 
Numero dell'opzione
60
Dicembre
 
"Identificatore della classe del fornitore". Nel mio caso, riporta la versione del client DHCP. Forse altri dispositivi restituiscono qualcosa di diverso. Windows, ad esempio, riporta MSFT 5.0

 
Lunghezza dell'opzione
11
Dicembre
 

 
Valore dell'opzione
udhcp 0.9.8
fila
 

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

01 — Maschera di rete
03 - Portale
06-DNS
oc: nome host
0f - nome di dominio di rete
1c - indirizzo della richiesta di trasmissione (broadcast)
42 - Nome del server TFTP
79 - Percorso statico senza classi

 
Lunghezza dell'opzione
8
 
1

 
Valore dell'opzione
01:03:06:0c:0f:1c:42:79
 
8

 
Numero dell'opzione
82
Dicembre
1
L'opzione 82 ripete ciò che è arrivato in DHCPDISCOVER

 
Lunghezza dell'opzione
18
Dicembre
1

 
Valore dell'opzione
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dicembre
18

 
Fine del pacchetto
255
Dicembre
1
255 simboleggia la fine del pacchetto

DHCPACK

A conferma che "sì, è vero, questo è il tuo indirizzo IP e non lo darò a nessun altro" dal server DHCP, serve un pacchetto in formato DHCPACK dal server al client. Viene inviato in broadcast proprio come gli altri pacchetti. Tuttavia, nel codice seguente per un server DHCP implementato in Python, per ogni evenienza, duplico qualsiasi richiesta di trasmissione inviando un pacchetto a un IP client specifico, se è già noto. Inoltre al server DHCP non interessa affatto se il pacchetto DHCPACK ha raggiunto il client. Se il client non riceve DHCPACK, dopo un po' ripete semplicemente DHCPREQUEST

Tabella della struttura dei pacchetti DHCPACK

Posizione nel pacchetto
Nome del valore (comune)
esempio
idea
byte
Chiarificazione

1
Richiesta di avvio
2
Hex
1
Tipo di messaggio. 1 - richiesta dal client al server, 2 - risposta dal server al client

2
Tipo di hardware
1
Hex
1
Tipo di indirizzo hardware, in questo protocollo 1 - MAC

3
Lunghezza degli indirizzi hardware
6
Hex
1
Lunghezza dell'indirizzo MAC del dispositivo

4
Luppolo
1
Hex
1
Numero di tratte intermedie

5
ID transazione
23:cf:de:1d
Hex
4
Identificatore univoco della transazione. Generato dal client all'inizio di un'operazione di richiesta

7
Il secondo è trascorso
0
Hex
4
Tempo in secondi dall'inizio del processo per ottenere un indirizzo

9
Flag di avvio
8000
Hex
2
Alcuni flag che possono essere impostati per indicare i parametri del protocollo. In questo caso è impostato “broadcast”.

11
Indirizzo IP del cliente
0.0.0.0
fila
4
Indirizzo IP del client (se presente)

15
L'indirizzo IP del tuo cliente
172.16.134.61
fila
4
Indirizzo IP offerto dal server (se disponibile)

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

23
Indirizzo IP dell'agente di inoltro
172.16.114.41
fila
4
Indirizzo IP dell'agente di inoltro (ad esempio, uno switch)

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

31
Riempimento dell'indirizzo hardware del client
 
Hex
10
Posto riservato. Di solito riempito con zeri

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

105
Nome del file di avvio
 
fila
128
Nome del file sul server utilizzato dalle stazioni diskless durante l'avvio

235
Biscotto magico
63: 82: 53: 63
Hex
4
Numero "magico", secondo il quale, incl. puoi determinare che questo pacchetto appartiene al protocollo DHCP

Opzioni DHCP. Può andare in qualsiasi ordine

236
Numero dell'opzione
53
Dicembre
3
Opzione 53, che definisce il tipo di pacchetto DHCP 5 - DHCPACK

 
Lunghezza dell'opzione
1
Dicembre
1

 
Valore dell'opzione
5
Dicembre
1

 
Numero dell'opzione
1
Dicembre
1
Opzione per offrire al client DHCP una maschera di rete

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
255.255.224.0
fila
4

 
Numero dell'opzione
3
Dicembre
1
Opzione per offrire al client DHCP un gateway predefinito

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
172.16.12.1
fila
4

 
Numero dell'opzione
6
Dicembre
1
Opzione per offrire DHCP al client DNS

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
8.8.8.8
fila
4

 
Numero dell'opzione
51
Dicembre
1
La durata in secondi dei parametri di rete emessi, dopodiché il client DHCP deve richiederli nuovamente

 
Lunghezza dell'opzione
4
Dicembre
1

 
Valore dell'opzione
86400
Dicembre
4

 
Numero dell'opzione
82
Dicembre
1
L'opzione 82 ripete ciò che è arrivato in DHCPDISCOVER

 
Lunghezza dell'opzione
18
Dicembre
1

 
Valore dell'opzione
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dicembre
18

 
Fine del pacchetto
255
Dicembre
1
255 simboleggia la fine del pacchetto

Installazione

L'installazione in realtà consiste nell'installare i moduli Python necessari al funzionamento. Si presuppone che MySQL sia già installato e configurato.

FreeBSD

pkg installa python3 python3 -m Guaranteepip pip3 installa il connettore mysql

Ubuntu

sudo apt-get install python3 sudo apt-get install pip3 sudo pip3 install mysql-connector

Creiamo un database MySQL, carichiamo al suo interno il dump pydhcp.sql e configuriamo il file di configurazione.

Configurazione

Tutte le impostazioni del server si trovano in un file xml. File di riferimento:

1.0 0.0.0.0 255.255.255.255 192.168.0.71 8600 1 255.255.255.0 192.168.0.1 localhost test test pydhcp opzione_8.8.8.8_hex:sw_porta82:1:20 opzione_22_hex:sw_port82:2:16 opzione_18_hex:sw_mac:82:26 40 seleziona ip, maschera, router, dns dagli utenti dove upper(mac)=upper('{option_3_AgentRemoteId_hex}') e upper(port)=upper('{option_1_AgentCircuitId_port_hex}') seleziona ip,maschera,router,dns dagli utenti dove upper(mac)=upper('{sw_mac}') e upper(porta)=upper('{sw_port82}') seleziona ip,maschera,router,dns dagli utenti dove upper(mac)=upper('{ClientMacAddress}') inserire nella cronologia (id,dt,mac,ip,comment) i valori (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')

Ora più in dettaglio sui tag:

La sezione dhcpserver descrive le impostazioni di base per l'avvio del server, ovvero:

  • host: quale indirizzo IP il server è in ascolto sulla porta 67
  • broadcast - quale ip è il broadcast per DHCPOFFER e DHCPACK
  • DHCPServer: qual è l'IP del server DHCP
  • LeaseTime durata del lease dell'indirizzo IP rilasciato
  • ThreadLimit: quanti thread sono in esecuzione simultaneamente per elaborare i pacchetti UDP in entrata sulla porta 67. Dovrebbe aiutare nei progetti ad alto carico 😉
  • defaultMask,defaultRouter,defaultDNS: cosa viene offerto all'abbonato per impostazione predefinita se viene trovato un IP nel database, ma per esso non sono specificati parametri aggiuntivi

sezione mysql:

host, nome utente, password, nome base: tutto parla da solo. Viene pubblicata una struttura approssimativa del database GitHub

Sezione query: le richieste per ricevere OFFER/ACK sono descritte qui:

  • offer_count — il numero di righe con richieste che restituiscono un risultato come ip,mask,router,dns
  • offer_n: stringa di query. Se return è vuoto, esegue la seguente richiesta di offerta
  • History_sql - una query che scrive, ad esempio, nella "cronologia delle autorizzazioni" per un abbonato

Le richieste possono includere qualsiasi variabile dalla sezione delle opzioni o opzioni dal protocollo DHCP.

Sezione Opzioni. È qui che diventa più interessante. Qui possiamo creare variabili che possiamo utilizzare più avanti nella sezione query.

Per esempio:

option_82_hex:sw_port1:20:22

, questa riga di comando prende l'intera riga fornita nell'opzione 82 della richiesta DHCP, in formato esadecimale, nell'intervallo da 20 a 22 byte inclusi e la inserisce nella nuova variabile sw_port1 (cambia porta da cui proviene la richiesta)

option_82_hex:sw_mac:26:40

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

Puoi vedere tutte le possibili opzioni che possono essere utilizzate nelle query avviando il server con l'opzione -d. Vedremo qualcosa di simile a questo registro:

--a pacchetto DHCPINFORM arrivato sulla porta 67, da 0025224ad764 , b'x91xa5xe0xa3xa5xa9-x8fx8a' , ('172.30.114.25', 68) {'ClientMacAddress': '0025224ad764', 'ClientMacAddressByte': b'x00 7%"J xd91d' , ' HType': 'Ethernet', 'HostName': b'x5xa0xe3xa5xa9xa8-x8fx43a', 'ReqListDNS': True, 'ReqListDomainName': True, 'ReqListPerfowmRouterDiscover': True, 'ReqListRouter': True, 'ReqListStaticRoute': True, 'ReqListSubnetM ask': True, 'ReqListVendorSpecInfo': 0.0.0.0, 'RequestedIpAddress': '5.0', 'Vendor': b'MSFT 0025224', 'chaddr': '764ad172.30.128.13', 'ciaddr': '00' , 'flags': b'x00x172.30.114.25', 'giaddr': '308', 'gpoz': 6, 'hlen': 1, 'hops': 82, 'htype': 'MAC', 'magic_cookie': b'cx12Sc ', 'op': 'DHCPINFORM', 'opzione12': 53, 'opzione53': 55, 'opzione55': 60, 'opzione60': 61, 'opzione61': 82, 'opzione82': 82, ' option_12_byte': b'x01x06x00x04x00x01x00x06x02x08x00x06' b'x00x1x9eXx2exb82xad', 'option_12010600040001000602080006001_hex': '589e2eb82ad', ' option_18_len': 82 12, 'option_01_str': "b'x06x00x04x00x01x00x06x02x08x00x06x00x1x9x2eXx768exb0.0.0.0xad'", 'risultato': False, 'secs': 001, 'siaddr': '589', 'sw_mac': '2e1eb06ad', 'sw_port89': '8', 'xidbyte': b'

Di conseguenza, possiamo racchiudere qualsiasi variabile in {} e verrà utilizzata nella query SQL.

Registriamo per la cronologia che il client ha ricevuto l'indirizzo IP:

Server DHCP+MySQL in Python

Server DHCP+MySQL in Python

Avvio del server

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

— d modalità di uscita della console DEBUG
- c <nome file> file di configurazione

debriefing

E ora maggiori dettagli sull'implementazione del server in Python. È un dolore. Python è stato imparato al volo. Molti momenti sono realizzati nello stile di “wow, in qualche modo l’ho fatto funzionare”. Non ottimizzato affatto e lasciato in questa forma principalmente a causa della scarsa esperienza nello sviluppo di Python. Mi soffermerò sugli aspetti più interessanti dell'implementazione del server in “codice”.

Analizzatore di file di configurazione XML

Viene utilizzato il modulo Python standard xml.dom. Sembra semplice, ma durante l'implementazione si è riscontrata una notevole mancanza di documentazione chiara ed esempi in rete sull'utilizzo di questo modulo.

    tree = minidom.parse(gconfig["config_file"]) mconfig=tree.getElementsByTagName("mysql") per 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") per 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") per 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") per elem nelle opzioni: node=elem.getElementsByTagName("option") per le opzioni nel nodo : opzioniMod.append(opzioni.firstChild.data)

Multithreading

Stranamente, il multithreading in Python è implementato in modo molto chiaro e semplice.

def PacketWork(data,addr): ... # implementazione dell'analisi del pacchetto in entrata e della risposta ad esso ... while True: data, addr = udp_socket.recvfrom(1024) # in attesa del pacchetto UDP thread = threading.Thread( target=PacketWork , args=(data,addr,)).start() # come è arrivato: lanciamo la funzione PacketWork precedentemente definita in background con parametri while threading.active_count() >gconfig["dhcp_ThreadLimit"]: time. sleep(1) # se il numero Ci sono più thread già in esecuzione rispetto alle impostazioni, aspettiamo finché non ce ne sono meno

Ricevere/inviare pacchetto DHCP

Per intercettare i pacchetti UDP in arrivo sulla scheda di rete è necessario “alzare” il socket:

udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"],67))

, dove le bandiere sono:

  • AF_INET - significa che il formato dell'indirizzo sarà IP: porta. Potrebbe anche esserci AF_UNIX, dove l'indirizzo è dato dal nome del file.
  • SOCK_DGRAM - significa che non accettiamo un "pacchetto grezzo", ma uno che è già passato attraverso il firewall e con un pacchetto parzialmente tagliato. Quelli. riceviamo solo un pacchetto UDP senza la componente “fisica” del wrapper del pacchetto UDP. Se utilizzi il flag SOCK_RAW, dovrai analizzare anche questo "wrapper".

L'invio di un pacchetto può essere come una trasmissione:

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

e all'indirizzo "da dove proviene il pacco":

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # cambia il socket in modalità multi-listener rz=udp_socket.sendto(packetack, addr)

, dove SOL_SOCKET indica il "livello di protocollo" per l'impostazione delle opzioni,

, opzione SO_BROADCAST che il pacchetto casco viene “trasmesso”

  L'opzione SO_REUSEADDR commuta il socket in modalità "molti ascoltatori". In teoria in questo caso non è necessario, ma su uno dei server FreeBSD su cui ho testato il codice non funzionava senza questa opzione.

Analisi di un pacchetto DHCP

È qui che mi è piaciuto davvero Python. Si scopre che fuori dagli schemi ti consente di essere abbastanza flessibile con il bytecode. Permettendogli di essere tradotto molto facilmente in valori decimali, stringhe ed esadecimali, ad es. questo è ciò di cui abbiamo effettivamente bisogno per comprendere la struttura del pacchetto. Quindi, ad esempio, puoi ottenere un intervallo di byte in HEX e solo byte:

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

, comprimere i byte in una struttura:

res["flags"]=pack('BB',dati[10],dati[11])

Ottieni 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"])

Per ora è tutto 😉

Fonte: habr.com

Aggiungi un commento