DHCP+Mysql-Server in Python

DHCP+Mysql-Server in Python

Ziel dieses Projekts war:

  • Studium des DHCP-Protokolls im IPv4-Netzwerk
  • Studium von Python (ein wenig mehr als von Grund auf 😉 )
  • Ersatz fĂŒr den Server DB2DHCP (mein Fork), Original hier, das unter einem neuen Betriebssystem immer schwieriger zu kompilieren ist. Außerdem gefĂ€llt mir nicht, dass ich das Binary nicht 'sofort' austauschen kann.
  • Erstellung eines betriebsbereiten DHCP-Servers mit der Möglichkeit, die IP-Adresse des Clients anhand der MAC-Adresse des Clients oder der Kombination aus MAC des Switches + Port (Option 82) auszuwĂ€hlen.
  • Schreiben eines weiteren Fahrrads (Oh! Das ist mein LieblingsbeschĂ€ftigung)
  • Erhalt von Kritik zu meiner Ungeschicklichkeit auf Habrhabr (oder besser noch eine Einladung) 😉

Ergebnis: funktioniert 😉 Erprobt auf FreeBSD und Ubuntu. Theoretisch könnte der Code auch unter jedem anderen Betriebssystem verwendet werden, da es anscheinend keine spezifischen Bindungen im Code gibt.
Achtung! Jetzt wird es viel.

Link zum Repository fĂŒr Liebhaber „live zu erleben“.

Der Prozess der Installation, Konfiguration und Verwendung des Ergebnisses des ‚Studiums der Materie‘ befindet sich viel tiefer, und dann etwas Theorie zum DHCP-Protokoll. FĂŒr mich. Und fĂŒr die Geschichte 😉

Ein wenig Theorie

Was ist DHCP

Das ist ein Netzwerkprotokoll, das einem GerĂ€t ermöglicht, seine IP-Adresse (und andere Parameter wie Gateway, DNS und mehr) vom DHCP-Server zu erfahren. Der Austausch von Paketen erfolgt ĂŒber das UDP-Protokoll. Das allgemeine Arbeitsprinzip eines GerĂ€ts bei der Anforderung von Netzparameter ist wie folgt:

  1. Das GerĂ€t (Client) sendet eine Broadcast-UDP-Anfrage (DHCPDISCOVER) im gesamten Netzwerk mit der Anfrage: ‚Na, jemand, gebt mir eine IP-Adresse‘. Üblicherweise (aber nicht immer) erfolgt die Anfrage von Port 68 (Quelle), wĂ€hrend die Zieladresse Port 67 ist (Ziel). Einige GerĂ€te senden Pakete auch von Port 67. Innerhalb des DHCPDISCOVER-Pakets ist die MAC-Adresse des Client-GerĂ€ts enthalten.
  2. Alle DHCP-Server im Netzwerk (und es können mehrere sein) formulieren ein Angebot (DHCPOFFER) mit Netzwerkeinstellungen fĂŒr das GerĂ€t, das DHCPDISCOVER gesendet hat, und senden es ebenfalls als Broadcast im Netzwerk. Die Identifizierung, an wen dieses Paket gerichtet ist, erfolgt ĂŒber die MAC-Adresse des Clients, die zuvor in der DHCPDISCOVER-Anfrage bereitgestellt wurde.
  3. Der Client empfĂ€ngt Pakete mit Angeboten fĂŒr Netzwerkkonfigurationen, wĂ€hlt die attraktivste aus (die Kriterien können unterschiedlich sein, beispielsweise auch die Lieferzeit des Pakets oder die Anzahl der Zwischennetzwerke), und sendet an den bevorzugten DHCP-Server eine offizielle DHCP-Anfrage DHCPREQUEST mit den Netzwerkkonfigurationen. In diesem Fall geht das Paket bereits an einen bestimmten DHCP-Server.
  4. Der Server, der die DHCPREQUEST erhalten hat, sendet ein DHCPACK-Paket, in dem erneut die fĂŒr diesen Client vorgesehenen Netzwerkkonfigurationen aufgefĂŒhrt sind.

DHCP+Mysql-Server in Python

DarĂŒber hinaus gibt es DHCPINFORM-Pakete, die vom Client gesendet werden und deren Ziel es ist, den DHCP-Server darĂŒber zu informieren, dass der "Client aktiv" ist und die ausgegebenen Netzwerkkonfigurationen nutzt. In dieser Serverimplementierung werden diese Pakete ignoriert.

Paketformat

Im Allgemeinen sieht das Ethernet-Paket folgendermaßen aus:

DHCP+Mysql-Server in Python

In unserem Fall betrachten wir nur die Daten des UDP-Paketinhalts, ohne die Header der OSI-Protokolle, nÀmlich die Struktur von DHCP:

DHCPDISCOVER

Der Prozess zum Erhalt einer IP-Adresse fĂŒr ein GerĂ€t beginnt damit, dass der DHCP-Client eine Broadcast-Anfrage von Port 68 an 255.255.255.255:67 sendet. In diesem Paket gibt der Client seine MAC-Adresse an und auch, was genau er vom DHCP-Server erhalten möchte. Die Paketstruktur wird in der nachstehenden Tabelle beschrieben.

Tabelle der Paketstruktur DHCPDISCOVER

Position im Paket
Name des Wertes
Beispiel
Darstellung
Byte
ErlÀuterung

1
Boot Anfrage
1
Hex
1
Nachrichtentyp. 1 — Anfrage vom Client an den Server, 2 — Antwort vom Server an den Client

2
Hardwaretyp
1
Hex
1
Typ der Hardwareadresse, in diesem Protokoll 1 — MAC

3
LĂ€nge der Hardwareadresse
6
Hex
1
LÀnge der MAC-Adresse des GerÀts

4
Hops
1
Hex
1
Anzahl der Zwischennetzwerke

5
Transaktions-ID
23:cf:de:1d
Hex
4
Eindeutige Transaktions-ID. Wird vom Client zu Beginn der Anforderungsoperation generiert.

7
Verstrichene Zeit in Sekunden
0
Hex
4
Zeit in Sekunden seit Beginn des Prozesses zur Erlangung der Adresse

9
Bootp-Flags
0
Hex
2
Einige Flags, die gesetzt werden können, um die Protokollparameter anzugeben

11
Client-IP-Adresse
0.0.0.0
Zeichenkette
4
Die IP-Adresse des Clients (falls vorhanden)

15
Ihre Client-IP-Adresse
0.0.0.0
Zeichenkette
4
Die vom Server vorgeschlagene IP-Adresse (falls vorhanden)

19
NĂ€chste Server-IP-Adresse
0.0.0.0
Zeichenkette
4
IP-Adresse des Servers (falls bekannt)

23
Relay-Agent-IP-Adresse
172.16.114.41
Zeichenkette
4
IP-Adresse des Relay-Agenten (z. B. Switch)

27
Client-MAC-Adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-Adresse des Paketabsenders (Client)

31
Client-HardwareadressenausfĂŒllung
 
Hex
10
Reservierter Platz. Normalerweise mit Nullen gefĂŒllt.

41
Server-Hostname
 
Zeichenkette
64
Name des DHCP-Servers. Normalerweise wird dies nicht ĂŒbertragen.

105
Boot-Dateiname
 
Zeichenkette
128
Dateiname auf dem Server, der von diskless Stations bei der Anmeldung verwendet wird

235
Magic-Cookie
63:82:53:63
Hex
4
Die "magische" Zahl, anhand derer unter anderem bestimmt werden kann, dass dieses Paket dem DHCP-Protokoll gehört

DHCP-Optionen. Können in beliebiger Reihenfolge auftreten

236
Optionsnummer
53
Dez
1
Option 53, die den Typ des DHCP-Pakets definiert

1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM

 
LĂ€nge der Option
1
Dez
1

 
Wert der Option
1
Dez
1

 
Optionsnummer
50
Dez
1
Welche IP-Adresse möchte der Client erhalten

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
172.16.134.61
Zeichenkette
4

 
Optionsnummer
55
 
1
Von dem Client angeforderte Netzwerkkonfigurationen. Die Zusammensetzung kann unterschiedlich sein

01 — Subnetzmaske
03 — Gateway
06 — DNS
oc — Hostname
0f — NetzwerkdomĂ€nenname
1c — Broadcast-Adresse (Brodcast)
42 — TFTP-Servername
79 — Classless Static Route

 
LĂ€nge der Option
8
 
1

 
Wert der Option
01:03:06:0c:0f:1c:42:79
 
8

 
Optionsnummer
82
Dez
 
Option 82, die die MAC-Adresse des GerĂ€tes — des Relais und einige zusĂ€tzliche Werte ĂŒbertrĂ€gt.

In der Regel — Port des Switches, an dem der Endclient fĂŒr DHCP arbeitet. In dieser Option sind zusĂ€tzliche Parameter "eingebettet". Das erste Byte ist die "Suboptionsnummer", das zweite ihre LĂ€nge, danach der Wert.

In diesem Fall sind in Option 82 Suboptionen eingebettet:
Agent Circuit ID = 00:04:00:01:00:04, wobei die letzten zwei Bytes der Port des DHCP-Clients sind, von dem die Anfrage kam

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC-Adresse des RelaisgerĂ€tes fĂŒr DHCP

 
LĂ€nge der Option
18
Dez
 

 
Wert der Option
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Ende des Pakets
255
Dez
1
255 symbolisiert das Ende des Pakets

DHCPOFFER

Sobald der Server das Paket DHCPDISCOVER erhĂ€lt und sieht, dass er dem Client etwas aus der Anfrage anbieten kann, erstellt er eine Antwort – DHCPDISCOVER. Die Antwort wird an den Port gesendet, von dem sie kam, broadcast-mĂ€ĂŸig, da der Client zu diesem Zeitpunkt noch keine IP-Adresse hat und das Paket daher nur akzeptieren kann, wenn es broadcast gesendet wird. Der Client erkennt, dass dies sein Paket ist, anhand seiner MAC-Adresse im Paket sowie der Transaktionsnummer, die er beim Erstellen des ersten Pakets generiert.

Tabelle der Paketstruktur DHCPOFFER

Position im Paket
Bezeichnung des Wertes (ĂŒblich)
Beispiel
Darstellung
Byte
ErlÀuterung

1
Boot Anfrage
1
Hex
1
Nachrichtentyp. 1 — Anfrage vom Client an den Server, 2 — Antwort vom Server an den Client

2
Hardwaretyp
1
Hex
1
Typ der Hardwareadresse, in diesem Protokoll 1 — MAC

3
LĂ€nge der Hardwareadresse
6
Hex
1
LÀnge der MAC-Adresse des GerÀts

4
Hops
1
Hex
1
Anzahl der Zwischennetzwerke

5
Transaktions-ID
23:cf:de:1d
Hex
4
Eindeutige Transaktions-ID. Wird vom Client zu Beginn der Anforderungsoperation generiert.

7
Verstrichene Zeit in Sekunden
0
Hex
4
Zeit in Sekunden seit Beginn des Prozesses zur Erlangung der Adresse

9
Bootp-Flags
0
Hex
2
Einige Flags, die gesetzt werden können, um Parameter des Protokolls anzuzeigen. In diesem Fall bedeutet 0 — Anfrage Typ Unicast

11
Client-IP-Adresse
0.0.0.0
Zeichenkette
4
Die IP-Adresse des Clients (falls vorhanden)

15
Ihre Client-IP-Adresse
172.16.134.61
Zeichenkette
4
Die vom Server vorgeschlagene IP-Adresse (falls vorhanden)

19
NĂ€chste Server-IP-Adresse
0.0.0.0
Zeichenkette
4
IP-Adresse des Servers (falls bekannt)

23
Relay-Agent-IP-Adresse
172.16.114.41
Zeichenkette
4
IP-Adresse des Relay-Agenten (z. B. Switch)

27
Client-MAC-Adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-Adresse des Paketabsenders (Client)

31
Client-HardwareadressenausfĂŒllung
 
Hex
10
Reservierter Platz. Normalerweise mit Nullen gefĂŒllt.

41
Server-Hostname
 
Zeichenkette
64
Name des DHCP-Servers. Normalerweise wird dies nicht ĂŒbertragen.

105
Boot-Dateiname
 
Zeichenkette
128
Dateiname auf dem Server, der von diskless Stations bei der Anmeldung verwendet wird

235
Magic-Cookie
63:82:53:63
Hex
4
Die "magische" Zahl, anhand derer unter anderem bestimmt werden kann, dass dieses Paket dem DHCP-Protokoll gehört

DHCP-Optionen. Können in beliebiger Reihenfolge auftreten

236
Optionsnummer
53
Dez
1
Option 53, die den Typ des DHCP-Pakets definiert 2 — DHCPOFFER

 
LĂ€nge der Option
1
Dez
1

 
Wert der Option
2
Dez
1

 
Optionsnummer
1
Dez
1
Option, die dem DHCP-Client eine Subnetzmaske anbietet

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
255.255.224.0
Zeichenkette
4

 
Optionsnummer
3
Dez
1
Option, die dem DHCP-Client ein Standardgateway anbietet

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
172.16.12.1
Zeichenkette
4

 
Optionsnummer
6
Dez
1
Option, die dem DHCP-Client einen DNS anbietet

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
8.8.8.8
Zeichenkette
4

 
Optionsnummer
51
Dez
1
Die Lebensdauer der vergebenen Netzwerkkonfigurationen in Sekunden, nach denen der DHCP-Client diese erneut anfordern muss

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
86400
Dez
4

 
Optionsnummer
82
Dez
1
Option 82, die wiederholt, was im DHCPDISCOVER empfangen wurde

 
LĂ€nge der Option
18
Dez
1

 
Wert der Option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dez
18

 
Ende des Pakets
255
Dez
1
255 symbolisiert das Ende des Pakets

DHCPREQUEST

Nachdem der Client das DHCPOFFER erhalten hat, erstellt er ein Paket mit der Anfrage der Netzwerkeinstellungen nicht fĂŒr alle DHCP-Server im Netzwerk, sondern nur fĂŒr einen bestimmten, dessen DHCPOFFER ihm am meisten "gefiel". Die Kriterien, die das "Gefallen" bestimmen, können unterschiedlich sein und hĂ€ngen von der Implementierung des DHCP-Clients ab. Der EmpfĂ€nger der Anfrage wird ĂŒber die MAC-Adresse des DHCP-Servers angegeben. Das DHCPREQUEST-Paket kann auch vom Client ohne vorherige Dhcpdiscover-Anfrage gesendet werden, wenn die IP-Adresse vom Server zuvor bereits erhalten wurde.

Tabelle der Struktur des DHCPREQUEST-Pakets

Position im Paket
Bezeichnung des Wertes (ĂŒblich)
Beispiel
Darstellung
Byte
ErlÀuterung

1
Boot Anfrage
1
Hex
1
Nachrichtentyp. 1 — Anfrage vom Client an den Server, 2 — Antwort vom Server an den Client

2
Hardwaretyp
1
Hex
1
Typ der Hardwareadresse, in diesem Protokoll 1 — MAC

3
LĂ€nge der Hardwareadresse
6
Hex
1
LÀnge der MAC-Adresse des GerÀts

4
Hops
1
Hex
1
Anzahl der Zwischennetzwerke

5
Transaktions-ID
23:cf:de:1d
Hex
4
Eindeutige Transaktions-ID. Wird vom Client zu Beginn der Anforderungsoperation generiert.

7
Verstrichene Zeit in Sekunden
0
Hex
4
Zeit in Sekunden seit Beginn des Prozesses zur Erlangung der Adresse

9
Bootp-Flags
8000
Hex
2
Einige Flags, die gesetzt werden können, um die Parameter des Protokolls anzugeben. In diesem Fall ist es auf "Broadcast" gesetzt.

11
Client-IP-Adresse
0.0.0.0
Zeichenkette
4
Die IP-Adresse des Clients (falls vorhanden)

15
Ihre Client-IP-Adresse
172.16.134.61
Zeichenkette
4
Die vom Server vorgeschlagene IP-Adresse (falls vorhanden)

19
NĂ€chste Server-IP-Adresse
0.0.0.0
Zeichenkette
4
IP-Adresse des Servers (falls bekannt)

23
Relay-Agent-IP-Adresse
172.16.114.41
Zeichenkette
4
IP-Adresse des Relay-Agenten (z. B. Switch)

27
Client-MAC-Adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-Adresse des Paketabsenders (Client)

31
Client-HardwareadressenausfĂŒllung
 
Hex
10
Reservierter Platz. Normalerweise mit Nullen gefĂŒllt.

41
Server-Hostname
 
Zeichenkette
64
Name des DHCP-Servers. Normalerweise wird dies nicht ĂŒbertragen.

105
Boot-Dateiname
 
Zeichenkette
128
Dateiname auf dem Server, der von diskless Stations bei der Anmeldung verwendet wird

235
Magic-Cookie
63:82:53:63
Hex
4
Die "magische" Zahl, anhand derer unter anderem bestimmt werden kann, dass dieses Paket dem DHCP-Protokoll gehört

DHCP-Optionen. Können in beliebiger Reihenfolge auftreten

236
Optionsnummer
53
Dez
3
Option 53, die den Typ des DHCP-Pakets 3 – DHCPREQUEST definiert

 
LĂ€nge der Option
1
Dez
1

 
Wert der Option
3
Dez
1

 
Optionsnummer
61
Dez
1
Kundenkennung: 01 (fĂŒr Ethernet) + MAC-Adresse des Clients

 
LĂ€nge der Option
7
Dez
1

 
Wert der Option
01:2c:ab:25:ff:72:a6
Hex
7

 
Optionsnummer
60
Dez
 
"Vendor class identifier". In meinem Fall gibt es die Version des DHCP-Clients an. Möglicherweise geben andere GerĂ€te etwas anderes zurĂŒck. Windows beispielsweise meldet MSFT 5.0.

 
LĂ€nge der Option
11
Dez
 

 
Wert der Option
udhcp 0.9.8
Zeichenkette
 

 
Optionsnummer
55
 
1
Von dem Client angeforderte Netzwerkkonfigurationen. Die Zusammensetzung kann unterschiedlich sein

01 — Subnetzmaske
03 — Gateway
06 — DNS
oc — Hostname
0f — NetzwerkdomĂ€nenname
1c — Broadcast-Adresse (Brodcast)
42 — TFTP-Servername
79 — Classless Static Route

 
LĂ€nge der Option
8
 
1

 
Wert der Option
01:03:06:0c:0f:1c:42:79
 
8

 
Optionsnummer
82
Dez
1
Option 82, die wiederholt, was im DHCPDISCOVER empfangen wurde

 
LĂ€nge der Option
18
Dez
1

 
Wert der Option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dez
18

 
Ende des Pakets
255
Dez
1
255 symbolisiert das Ende des Pakets

DHCPACK

Als BestĂ€tigung, dass "ja, das ist deine IP-Adresse, und ich werde sie niemand anderem geben", dient das Paket im Format DHCPACK vom DHCP-Server an den Client. Es wird, wie die anderen Pakete, ebenfalls broadcasted. Allerdings dupliziere ich im folgenden Code des in Python implementierten DHCP-Servers vorsichtshalber jede Broadcast-Anfrage, indem ich ein Paket an eine bestimmte IP-Adresse des Clients sende, wenn diese bereits bekannt ist. Der DHCP-Server interessiert sich jedoch ĂŒberhaupt nicht dafĂŒr, ob das DHCPACK-Paket den Client erreicht hat. ErhĂ€lt der Client kein DHCPACK, wiederholt er einfach nach einer gewissen Zeit das DHCPREQUEST.

Tabelle der Struktur des DHCPACK-Pakets

Position im Paket
Bezeichnung des Wertes (ĂŒblich)
Beispiel
Darstellung
Byte
ErlÀuterung

1
Boot Anfrage
2
Hex
1
Nachrichtentyp. 1 — Anfrage vom Client an den Server, 2 — Antwort vom Server an den Client

2
Hardwaretyp
1
Hex
1
Typ der Hardwareadresse, in diesem Protokoll 1 — MAC

3
LĂ€nge der Hardwareadresse
6
Hex
1
LÀnge der MAC-Adresse des GerÀts

4
Hops
1
Hex
1
Anzahl der Zwischennetzwerke

5
Transaktions-ID
23:cf:de:1d
Hex
4
Eindeutige Transaktions-ID. Wird vom Client zu Beginn der Anforderungsoperation generiert.

7
Verstrichene Zeit in Sekunden
0
Hex
4
Zeit in Sekunden seit Beginn des Prozesses zur Erlangung der Adresse

9
Bootp-Flags
8000
Hex
2
Einige Flags, die gesetzt werden können, um die Parameter des Protokolls anzugeben. In diesem Fall ist es auf "Broadcast" gesetzt.

11
Client-IP-Adresse
0.0.0.0
Zeichenkette
4
Die IP-Adresse des Clients (falls vorhanden)

15
Ihre Client-IP-Adresse
172.16.134.61
Zeichenkette
4
Die vom Server vorgeschlagene IP-Adresse (falls vorhanden)

19
NĂ€chste Server-IP-Adresse
0.0.0.0
Zeichenkette
4
IP-Adresse des Servers (falls bekannt)

23
Relay-Agent-IP-Adresse
172.16.114.41
Zeichenkette
4
IP-Adresse des Relay-Agenten (z. B. Switch)

27
Client-MAC-Adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-Adresse des Paketabsenders (Client)

31
Client-HardwareadressenausfĂŒllung
 
Hex
10
Reservierter Platz. Normalerweise mit Nullen gefĂŒllt.

41
Server-Hostname
 
Zeichenkette
64
Name des DHCP-Servers. Normalerweise wird dies nicht ĂŒbertragen.

105
Boot-Dateiname
 
Zeichenkette
128
Dateiname auf dem Server, der von diskless Stations bei der Anmeldung verwendet wird

235
Magic-Cookie
63:82:53:63
Hex
4
Die "magische" Zahl, anhand derer unter anderem bestimmt werden kann, dass dieses Paket dem DHCP-Protokoll gehört

DHCP-Optionen. Können in beliebiger Reihenfolge auftreten

236
Optionsnummer
53
Dez
3
Option 53, die den Typ des DHCP-Pakets 5 – DHCPACK definiert

 
LĂ€nge der Option
1
Dez
1

 
Wert der Option
5
Dez
1

 
Optionsnummer
1
Dez
1
Option, die dem DHCP-Client eine Subnetzmaske anbietet

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
255.255.224.0
Zeichenkette
4

 
Optionsnummer
3
Dez
1
Option, die dem DHCP-Client ein Standardgateway anbietet

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
172.16.12.1
Zeichenkette
4

 
Optionsnummer
6
Dez
1
Option, die dem DHCP-Client einen DNS anbietet

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
8.8.8.8
Zeichenkette
4

 
Optionsnummer
51
Dez
1
Die Lebensdauer der vergebenen Netzwerkkonfigurationen in Sekunden, nach denen der DHCP-Client diese erneut anfordern muss

 
LĂ€nge der Option
4
Dez
1

 
Wert der Option
86400
Dez
4

 
Optionsnummer
82
Dez
1
Option 82, die wiederholt, was im DHCPDISCOVER empfangen wurde

 
LĂ€nge der Option
18
Dez
1

 
Wert der Option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dez
18

 
Ende des Pakets
255
Dez
1
255 symbolisiert das Ende des Pakets

Installation

Die Einrichtung besteht tatsÀchlich darin, die benötigten Python-Module zu installieren. Es wird vorausgesetzt, dass MySQL bereits installiert und konfiguriert ist.

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

Wir erstellen eine MySQL-Datenbank, laden das Dump pydhcp.sql hoch und konfigurieren die Konfigurationsdatei.

Konfiguration

Alle Servereinstellungen befinden sich in einer XML-Datei. Referenzdatei:

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')

Jetzt detaillierter zu den Tags:

Der Abschnitt dhcpserver beschreibt die grundlegenden Einstellungen zum Starten des Servers, nÀmlich:

  • host — welche IP-Adresse der Server am Port 67 abfĂ€ngt.
  • broadcast — welche IP-Adresse fĂŒr DHCPOFFER und DHCPACK verwendet wird.
  • DHCPServer — welche IP-Adresse der DHCP-Server hat.
  • LeaseTime — die Zeit, fĂŒr die die zugewiesene IP-Adresse gĂŒltig ist.
  • ThreadLimit — wie viele Threads gleichzeitig zur Verarbeitung der eingehenden UDP-Pakete am Port 67 aktiv sind. Es wird angenommen, dass dies bei stark ausgelasteten Projekten hilft 😉.
  • defaultMask, defaultRouter, defaultDNS — die Standardwerte, die dem Abonnenten angeboten werden, wenn die IP in der Datenbank gefunden wird, aber keine zusĂ€tzlichen Parameter angegeben sind.

Abschnitt mysql:

host, username, password, basename — alles erklĂ€rt sich von selbst. Eine Beispielform der Datenbankstruktur wird bereitgestellt auf GitHub

Abschnitt query: Hier sind die Abfragen zur Beschaffung von OFFER/ACK beschrieben:

  • offer_count — die Anzahl der Zeilen mit Anfragen, die ein Ergebnis in der Form ip, mask, router, dns zurĂŒckgeben.
  • offer_n — Anfragenzeile. Wenn die RĂŒckgabe leer ist, wird die nĂ€chste Anfrage offer ausgefĂŒhrt.
  • history_sql — eine Abfrage, die beispielsweise in die 'Authentifizierungshistorie' fĂŒr den Abonnenten schreibt.

In den Abfragen können beliebige Variablen aus dem Abschnitt options oder Optionen aus dem DHCP-Protokoll verwendet werden.

Abschnitt options. Hier wird es interessanter. Hier können wir Variablen erstellen, die wir spÀter im Abschnitt query verwenden können.

Zum Beispiel:

option_82_hex:sw_port1:20:22

, dieser Befehl nimmt die gesamte Zeile aus der DHCP-Anfrage Option 82 im Hex-Format im Bereich von Byte 20 bis 22 (einschließlich) und legt sie in einer neuen Variablen sw_port1 ab (Switch-Port, von dem die Anfrage kam)

option_82_hex:sw_mac:26:40

, wir definieren die Variable sw_mac, indem wir den Hex-Wert aus dem Bereich 26:40 nehmen

Um alle möglichen Optionen zu sehen, die in Anfragen verwendet werden können, kann der Server mit dem SchlĂŒssel -d gestartet werden. Wir sehen etwa folgendes Protokoll:

--es kam ein Paket DHCPINFORM an Port 67 von 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,
 'ReqListRouterDiscover': 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'}

Daher können wir jede Variable in {} einschließen, und sie wird im SQL-Abfrage verwendet.

Lassen Sie uns zur Dokumentation festhalten, welche IP-Adresse der Client erhalten hat:

DHCP+Mysql-Server in Python

DHCP+Mysql-Server in Python

Server starten

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

— d Modus fĂŒr DEBUG-Ausgabe in der Konsole
— c Konfigurationsdatei

ÜberprĂŒfung der Funktionsweise

Nun also ausfĂŒhrlicher zur Umsetzung des Servers in Python. Das ist schmerzhaft. Python wurde "on the fly" gelernt. Viele Aspekte wurden nach dem Motto umgesetzt: "Wow, das habe ich irgendwie gemacht, es funktioniert." Völlig unoptimiert und in dieser Form belassen, hauptsĂ€chlich wegen geringer Erfahrung in der Python-Entwicklung. Ich werde mich auf die interessantesten Aspekte der Server-Implementierung im "Code" konzentrieren.

Parser fĂŒr die XML-Konfigurationsdatei

Es wird das Standardmodul Python xml.dom verwendet. Es scheint einfach zu sein, aber bei der Implementierung hat es merklich an brauchbarer Dokumentation und Beispielen im Netz gefehlt, die dieses Modul verwenden.

    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

Es ist ĂŒberraschend, dass Multithreading in Python sehr klar und einfach umgesetzt ist.

def PacketWork(data, addr): 
...
# Implementierung der Analyse des eingehenden Pakets und der Antwort darauf
...


while True:
    data, addr = udp_socket.recvfrom(1024) # Warten auf ein UDP-Paket
    thread = threading.Thread(target=PacketWork, args=(data, addr,)).start()	# Sobald es ankommt, starten wir die vorher definierte Funktion PacketWork im Hintergrund mit den Parametern
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # Wenn die Anzahl der bereits gestarteten Threads höher ist als in den Einstellungen, warten wir, bis es weniger werden

Empfang/Versand von DHCP-Paketen

Um UDP-Pakete, die ĂŒber die Netzwerkkarte gehen, abzufangen, muss der Socket "hochgefahren" werden:

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

, wobei die Flags:

  • AF_INET bedeutet, dass das Adressformat IP:Port sein wird. Es kann auch AF_UNIX geben — wobei die Adresse durch einen Dateinamen angegeben wird.
  • SOCK_DGRAM bedeutet, dass wir kein „rohes Paket“ empfangen, sondern bereits ein Paket, das durch die Firewall gefiltert und teilweise abgeschnitten wurde. Das heißt, wir erhalten nur das UDP-Paket ohne die „physische“ Verpackung des UDP-Pakets. Wenn das FLAG SOCK_RAW verwendet wird, muss auch diese „Verpackung“ geparst werden.

Der Versand eines Pakets kann sowohl per Broadcast erfolgen:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) #legen den Socket in den Broadcast-Modus
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, als auch an die Adresse, „von der das Paket kam“:

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #legen den Socket in den Modus "mehrere Zuhörer"
                        rz=udp_socket.sendto(packetack, addr)

, wobei SOL_SOCKET „Protokollebene“ zum Setzen der Optionen bedeutet,

, SO_BROADCAST die Option ist, dass das Paket „per Broadcast“ gesendet wird,

  , SO_REUSEADDR ist die Option, die den Socket in den Modus „viele Zuhörer“ wechselt. Theoretisch ist sie in diesem Fall nicht erforderlich, aber auf einem der FreeBSD-Server, auf dem ich getestet habe, funktionierte der Code ohne diese Option nicht.

Analyse des DHCP-Pakets

Hier hat mir Python wirklich gefallen. Es stellt sich heraus, dass es aus der „Box“ recht flexibel mit Bytecode umgehen kann. Es ermöglicht, diesen sehr einfach in dezimale Werte, Strings und Hex zu ĂŒbersetzen – also genau das, was wir brauchen, um die Struktur des Pakets zu verstehen. So kann man beispielsweise einen Bereich von Bytes in HEX und einfach in Bytes erhalten:

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

, die Bytes in eine Struktur packen:

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

Die IP aus der Struktur erhalten:

res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));

Und umgekehrt:

res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])

Das ist alles 😉

Quelle: habr.com

60GB SSD 8Gb DDR4