DHCP+MySQL-Server in Python

DHCP+MySQL-Server in Python

Der Zweck dieses Projekts war:

  • Erfahren Sie mehr über DHCP in einem IPv4-Netzwerk
  • Python lernen (etwas mehr als von Grund auf 😉)
  • Serveraustausch DB2DHCP (meine Gabel), original hier, was für das neue Betriebssystem immer schwieriger zu montieren ist. Und es gefällt mir nicht, dass es sich um eine Binärdatei handelt, die „im Moment nicht geändert“ werden kann.
  • Erhalten eines funktionierenden DHCP-Servers mit der Möglichkeit, die IP-Adresse eines Abonnenten mithilfe der Mac- oder Switch-Mac+Port-Kombination des Abonnenten auszuwählen (Option 82)
  • Ein weiteres Fahrrad schreiben (Oh! Das ist meine Lieblingsbeschäftigung)
  • Erhalte Kommentare zu deiner Klugheit auf Habrahabr (oder noch besser, eine Einladung) 😉

Ergebnis: Es funktioniert 😉 Getestet unter FreeBSD und Ubuntu OS. Theoretisch kann der Code aufgefordert werden, unter jedem Betriebssystem zu funktionieren, weil Der Code scheint keine spezifischen Bindungen zu enthalten.
Sorgfältig! Es kommt noch viel mehr.

Link zum Repository für Amateure „lebendig berühren“.

Der Installations-, Konfigurations- und Verwendungsprozess ist viel niedriger als das Ergebnis des „Studiums der Hardware“ und dann ein wenig Theorie über das DHCP-Protokoll. Für sich. Und für die Geschichte 😉

Eine kleine Theorie

Was ist DHCP?

Hierbei handelt es sich um ein Netzwerkprotokoll, das es einem Gerät ermöglicht, seine IP-Adresse (und andere Parameter wie Gateway, DNS usw.) von einem DHCP-Server herauszufinden. Pakete werden über das UDP-Protokoll ausgetauscht. Das allgemeine Funktionsprinzip des Geräts beim Anfordern von Netzwerkparametern ist wie folgt:

  1. Das Gerät (Client) sendet eine UDP-Broadcast-Anfrage (DHCPDISCOVER) im gesamten Netzwerk mit der Aufforderung „Nun, jemand gibt mir eine IP-Adresse.“ Darüber hinaus erfolgt die Anforderung normalerweise (aber nicht immer) von Port 68 (Quelle) und das Ziel ist Port 67 (Ziel). Einige Geräte versenden Pakete auch über Port 67. Die MAC-Adresse des Clientgeräts ist im DHCPDISCOVER-Paket enthalten.
  2. Alle im Netzwerk befindlichen DHCP-Server (und es können mehrere davon sein) bilden ein DHCPOFFER-Angebot mit Netzwerkeinstellungen für das Gerät, das DHCPDISCOVER gesendet hat, und senden es auch über das Netzwerk. Die Identifizierung, für wen dieses Paket bestimmt ist, basiert auf der MAC-Adresse des Clients, die zuvor in der DHCPDISCOVER-Anfrage angegeben wurde.
  3. Der Client akzeptiert Pakete mit Vorschlägen für Netzwerkeinstellungen, wählt das attraktivste aus (die Kriterien können unterschiedlich sein, z. B. der Zeitpunkt der Paketzustellung, die Anzahl der Zwischenrouten) und stellt eine „offizielle Anfrage“ DHCPREQUEST mit den Netzwerkeinstellungen vom DHCP-Server, den es mag. In diesem Fall geht das Paket an einen bestimmten DHCP-Server.
  4. Der Server, der den DHCPREQUEST empfangen hat, sendet ein Paket im DHCPACK-Format, in dem er noch einmal die für diesen Client vorgesehenen Netzwerkeinstellungen auflistet

DHCP+MySQL-Server in Python

Darüber hinaus gibt es DHCPINFORM-Pakete, die vom Client kommen und deren Zweck darin besteht, den DHCP-Server darüber zu informieren, dass der „Client aktiv“ ist und die ausgegebenen Netzwerkeinstellungen verwendet. In der Implementierung dieses Servers werden diese Pakete ignoriert.

Paketformat

Im Allgemeinen sieht ein Ethernet-Paketrahmen etwa so aus:

DHCP+MySQL-Server in Python

In unserem Fall betrachten wir nur die Daten direkt aus dem Inhalt des UDP-Pakets, ohne Protokollheader der OSI-Schicht, nämlich die DHCP-Struktur:

DHCPENTDECKEN

Der Prozess zum Erhalten einer IP-Adresse für ein Gerät beginnt also 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 sowie den genauen Inhalt an, den er vom DHCP-Server erhalten möchte. Die Paketstruktur ist in der folgenden Tabelle beschrieben.

DHCPDISCOVER-Paketstrukturtabelle

Position im Paket
Wertname
Beispiel
Einleitung
Byte
Klärung

1
Boot-Anfrage
1
Sechskant
1
Nachrichtentyp. 1 – Anfrage vom Client an den Server, 2 – Antwort vom Server an den Client

2
Hardwaretyp
1
Sechskant
1
Art der Hardwareadresse, in diesem Protokoll 1 – MAC

3
Länge der Hardwareadressen
6
Sechskant
1
Länge der MAC-Adresse des Geräts

4
Hopfen
1
Sechskant
1
Anzahl der Zwischenrouten

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

7
Sekunde verstrichen
0
Sechskant
4
Zeit in Sekunden seit Beginn des Adressbeschaffungsprozesses

9
Boot-Flags
0
Sechskant
2
Bestimmte Flags, die zur Angabe von Protokollparametern gesetzt werden können

11
Client-IP-Adresse
0.0.0.0
String
4
Client-IP-Adresse (falls vorhanden)

15
Ihre Client-IP-Adresse
0.0.0.0
String
4
Vom Server angebotene IP-Adresse (falls verfügbar)

19
Nächste Server-IP-Adresse
0.0.0.0
String
4
Server-IP-Adresse (falls bekannt)

23
IP-Adresse des Relay-Agenten
172.16.114.41
String
4
IP-Adresse des Relay-Agenten (z. B. eines Switches)

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

31
Auffüllen der Client-Hardwareadresse
 
Sechskant
10
Reservierter Platz. Normalerweise mit Nullen gefüllt

41
Hostname des Servers
 
String
64
Name des DHCP-Servers. Normalerweise nicht übermittelt

105
Name der Boot-Datei
 
String
128
Dateiname auf dem Server, der von diskless Stationen beim Booten verwendet wird

235
Magischer Keks
63: 82: 53: 63
Sechskant
4
„Magische“ Zahl, nach der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört

DHCP-Optionen. Kann in beliebiger Reihenfolge erfolgen

236
Optionsnummer
53
Dezember
1
Option 53, die den DHCP-Pakettyp angibt

1 – DHCPDISCOVER
3 – DHCPREQUEST
2 - DHCPANGEBOT
5 – DHCPACK
8 - DHCPINFORM

 
Optionslänge
1
Dezember
1

 
Optionswert
1
Dezember
1

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

 
Optionslänge
4
Dezember
1

 
Optionswert
172.16.134.61
String
4

 
Optionsnummer
55
 
1
Vom Client angeforderte Netzwerkparameter. Die Zusammensetzung kann variieren

01 – Netzwerkmaske
03 – Tor
06 – DNS
oc – Hostname
0f – Netzwerkdomänenname
1c – Adresse der Broadcast-Anfrage (Broadcast)
42 – TFTP-Servername
79 – Klassenlose statische Route

 
Optionslänge
8
 
1

 
Optionswert
01:03:06:0c:0f:1c:42:79
 
8

 
Optionsnummer
82
Dezember
 
Option 82, die die MAC-Adresse des Repeater-Geräts und einige zusätzliche Werte übermittelt.

Meistens ist dies der Port des Switches, auf dem der End-DHCP-Client läuft. Diese Option enthält zusätzliche Parameter. Das erste Byte ist die Nummer der „Unteroption“, das zweite ist ihre Länge und dann ihr Wert.

In diesem Fall sind in Option 82 die Unteroptionen verschachtelt:
Agent Circuit ID = 00:04:00:01:00:04, wobei die letzten beiden Bytes der DHCP-Client-Port sind, von dem die Anfrage kam

Agent Remote ID = 00:06:c8:be:19:93:11:48 – MAC-Adresse des DHCP-Repeater-Geräts

 
Optionslänge
18
Dezember
 

 
Optionswert
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Sechskant
 

 
Ende des Pakets
255
Dezember
1
255 symbolisiert das Ende des Pakets

DHCPANGEBOT

Sobald der Server das DHCPDISCOVER-Paket empfängt und sieht, dass er dem Client etwas von dem angeforderten Paket anbieten kann, generiert er eine Antwort dafür – DHCPDISCOVER. Die Antwort wird per Broadcast an den Port gesendet, „von dem sie kam“, weil Da der Client zu diesem Zeitpunkt noch keine IP-Adresse hat, kann er das Paket nur annehmen, wenn es per Broadcast versendet wird. Dass es sich um ein Paket für ihn handelt, erkennt der Kunde an seiner MAC-Adresse im Paket sowie an der Transaktionsnummer, die er bei der Erstellung des ersten Pakets generiert.

DHCPOFFER-Paketstrukturtabelle

Position im Paket
Name des Werts (allgemein)
Beispiel
Einleitung
Byte
Klärung

1
Boot-Anfrage
1
Sechskant
1
Nachrichtentyp. 1 – Anfrage vom Client an den Server, 2 – Antwort vom Server an den Client

2
Hardwaretyp
1
Sechskant
1
Art der Hardwareadresse, in diesem Protokoll 1 – MAC

3
Länge der Hardwareadressen
6
Sechskant
1
Länge der MAC-Adresse des Geräts

4
Hopfen
1
Sechskant
1
Anzahl der Zwischenrouten

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

7
Sekunde verstrichen
0
Sechskant
4
Zeit in Sekunden seit Beginn des Adressbeschaffungsprozesses

9
Boot-Flags
0
Sechskant
2
Bestimmte Flags, die zur Angabe von Protokollparametern gesetzt werden können. In diesem Fall bedeutet 0 den Unicast-Anfragetyp

11
Client-IP-Adresse
0.0.0.0
String
4
Client-IP-Adresse (falls vorhanden)

15
Ihre Client-IP-Adresse
172.16.134.61
String
4
Vom Server angebotene IP-Adresse (falls verfügbar)

19
Nächste Server-IP-Adresse
0.0.0.0
String
4
Server-IP-Adresse (falls bekannt)

23
IP-Adresse des Relay-Agenten
172.16.114.41
String
4
IP-Adresse des Relay-Agenten (z. B. eines Switches)

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

31
Auffüllen der Client-Hardwareadresse
 
Sechskant
10
Reservierter Platz. Normalerweise mit Nullen gefüllt

41
Hostname des Servers
 
String
64
Name des DHCP-Servers. Normalerweise nicht übermittelt

105
Name der Boot-Datei
 
String
128
Dateiname auf dem Server, der von diskless Stationen beim Booten verwendet wird

235
Magischer Keks
63: 82: 53: 63
Sechskant
4
„Magische“ Zahl, nach der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört

DHCP-Optionen. Kann in beliebiger Reihenfolge erfolgen

236
Optionsnummer
53
Dezember
1
Option 53, die den DHCP 2-Pakettyp definiert – DHCPOFFER

 
Optionslänge
1
Dezember
1

 
Optionswert
2
Dezember
1

 
Optionsnummer
1
Dezember
1
Option, dem DHCP-Client eine Netzwerkmaske anzubieten

 
Optionslänge
4
Dezember
1

 
Optionswert
255.255.224.0
String
4

 
Optionsnummer
3
Dezember
1
Option, dem DHCP-Client ein Standard-Gateway anzubieten

 
Optionslänge
4
Dezember
1

 
Optionswert
172.16.12.1
String
4

 
Optionsnummer
6
Dezember
1
Option zum Anbieten von DHCP für den DNS-Client

 
Optionslänge
4
Dezember
1

 
Optionswert
8.8.8.8
String
4

 
Optionsnummer
51
Dezember
1
Die Lebensdauer der ausgegebenen Netzwerkparameter in Sekunden, nach deren Ablauf der DHCP-Client sie erneut anfordern muss

 
Optionslänge
4
Dezember
1

 
Optionswert
86400
Dezember
4

 
Optionsnummer
82
Dezember
1
Option 82 wiederholt, was in DHCPDISCOVER kam

 
Optionslänge
18
Dezember
1

 
Optionswert
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dezember
18

 
Ende des Pakets
255
Dezember
1
255 symbolisiert das Ende des Pakets

DHCPANFRAGE

Nachdem der Client DHCPOFFER erhalten hat, bildet er ein Paket mit der Anforderung von Netzwerkparametern nicht an alle DHCP-Server im Netzwerk, sondern nur an einen bestimmten Server, dessen DHCPOFFER-Angebot ihm am besten gefallen hat. Die „Gefällt mir“-Kriterien können unterschiedlich sein und hängen von der DHCP-Implementierung des Clients ab. Der Empfänger der Anfrage wird über die MAC-Adresse des DHCP-Servers angegeben. Außerdem kann der Client ein DHCPREQUEST-Paket senden, ohne zuvor DHCPDISCOVER zu generieren, wenn die IP-Adresse des Servers bereits zuvor erhalten wurde.

DHCPREQUEST-Paketstrukturtabelle

Position im Paket
Name des Werts (allgemein)
Beispiel
Einleitung
Byte
Klärung

1
Boot-Anfrage
1
Sechskant
1
Nachrichtentyp. 1 – Anfrage vom Client an den Server, 2 – Antwort vom Server an den Client

2
Hardwaretyp
1
Sechskant
1
Art der Hardwareadresse, in diesem Protokoll 1 – MAC

3
Länge der Hardwareadressen
6
Sechskant
1
Länge der MAC-Adresse des Geräts

4
Hopfen
1
Sechskant
1
Anzahl der Zwischenrouten

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

7
Sekunde verstrichen
0
Sechskant
4
Zeit in Sekunden seit Beginn des Adressbeschaffungsprozesses

9
Boot-Flags
8000
Sechskant
2
Bestimmte Flags, die zur Angabe von Protokollparametern gesetzt werden können. In diesem Fall ist „Broadcast“ eingestellt

11
Client-IP-Adresse
0.0.0.0
String
4
Client-IP-Adresse (falls vorhanden)

15
Ihre Client-IP-Adresse
172.16.134.61
String
4
Vom Server angebotene IP-Adresse (falls verfügbar)

19
Nächste Server-IP-Adresse
0.0.0.0
String
4
Server-IP-Adresse (falls bekannt)

23
IP-Adresse des Relay-Agenten
172.16.114.41
String
4
IP-Adresse des Relay-Agenten (z. B. eines Switches)

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

31
Auffüllen der Client-Hardwareadresse
 
Sechskant
10
Reservierter Platz. Normalerweise mit Nullen gefüllt

41
Hostname des Servers
 
String
64
Name des DHCP-Servers. Normalerweise nicht übermittelt

105
Name der Boot-Datei
 
String
128
Dateiname auf dem Server, der von diskless Stationen beim Booten verwendet wird

235
Magischer Keks
63: 82: 53: 63
Sechskant
4
„Magische“ Zahl, nach der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört

DHCP-Optionen. Kann in beliebiger Reihenfolge erfolgen

236
Optionsnummer
53
Dezember
3
Option 53, die den DHCP-Pakettyp 3 definiert – DHCPREQUEST

 
Optionslänge
1
Dezember
1

 
Optionswert
3
Dezember
1

 
Optionsnummer
61
Dezember
1
Client-ID: 01 (für Ethernet) + Client-MAC-Adresse

 
Optionslänge
7
Dezember
1

 
Optionswert
01:2c:ab:25:ff:72:a6
Sechskant
7

 
Optionsnummer
60
Dezember
 
„Anbieterklassen-ID“. In meinem Fall wird die DHCP-Client-Version gemeldet. Möglicherweise geben andere Geräte etwas anderes zurück. Windows meldet beispielsweise MSFT 5.0

 
Optionslänge
11
Dezember
 

 
Optionswert
0.9.8
String
 

 
Optionsnummer
55
 
1
Vom Client angeforderte Netzwerkparameter. Die Zusammensetzung kann variieren

01 – Netzwerkmaske
03 – Tor
06 – DNS
oc – Hostname
0f – Netzwerkdomänenname
1c – Adresse der Broadcast-Anfrage (Broadcast)
42 – TFTP-Servername
79 – Klassenlose statische Route

 
Optionslänge
8
 
1

 
Optionswert
01:03:06:0c:0f:1c:42:79
 
8

 
Optionsnummer
82
Dezember
1
Option 82 wiederholt, was in DHCPDISCOVER kam

 
Optionslänge
18
Dezember
1

 
Optionswert
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dezember
18

 
Ende des Pakets
255
Dezember
1
255 symbolisiert das Ende des Pakets

DHCPACK

Als Bestätigung des DHCP-Servers „Ja, das ist richtig, das ist Ihre IP-Adresse und ich gebe sie nicht an andere weiter“ dient ein Paket im DHCPACK-Format vom Server an den Client. Es wird wie andere Pakete im Broadcast-Verfahren versendet. Allerdings dupliziere ich im folgenden Code für einen in Python implementierten DHCP-Server für alle Fälle jede Broadcast-Anfrage, indem ich ein Paket an eine bestimmte Client-IP sende, sofern diese bereits bekannt ist. Darüber hinaus ist es dem DHCP-Server völlig egal, ob das DHCPACK-Paket den Client erreicht hat. Wenn der Client kein DHCPACK erhält, wiederholt er nach einer Weile einfach DHCPREQUEST

DHCPACK-Paketstrukturtabelle

Position im Paket
Name des Werts (allgemein)
Beispiel
Einleitung
Byte
Klärung

1
Boot-Anfrage
2
Sechskant
1
Nachrichtentyp. 1 – Anfrage vom Client an den Server, 2 – Antwort vom Server an den Client

2
Hardwaretyp
1
Sechskant
1
Art der Hardwareadresse, in diesem Protokoll 1 – MAC

3
Länge der Hardwareadressen
6
Sechskant
1
Länge der MAC-Adresse des Geräts

4
Hopfen
1
Sechskant
1
Anzahl der Zwischenrouten

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

7
Sekunde verstrichen
0
Sechskant
4
Zeit in Sekunden seit Beginn des Adressbeschaffungsprozesses

9
Boot-Flags
8000
Sechskant
2
Bestimmte Flags, die zur Angabe von Protokollparametern gesetzt werden können. In diesem Fall ist „Broadcast“ eingestellt

11
Client-IP-Adresse
0.0.0.0
String
4
Client-IP-Adresse (falls vorhanden)

15
Ihre Client-IP-Adresse
172.16.134.61
String
4
Vom Server angebotene IP-Adresse (falls verfügbar)

19
Nächste Server-IP-Adresse
0.0.0.0
String
4
Server-IP-Adresse (falls bekannt)

23
IP-Adresse des Relay-Agenten
172.16.114.41
String
4
IP-Adresse des Relay-Agenten (z. B. eines Switches)

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

31
Auffüllen der Client-Hardwareadresse
 
Sechskant
10
Reservierter Platz. Normalerweise mit Nullen gefüllt

41
Hostname des Servers
 
String
64
Name des DHCP-Servers. Normalerweise nicht übermittelt

105
Name der Boot-Datei
 
String
128
Dateiname auf dem Server, der von diskless Stationen beim Booten verwendet wird

235
Magischer Keks
63: 82: 53: 63
Sechskant
4
„Magische“ Zahl, nach der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört

DHCP-Optionen. Kann in beliebiger Reihenfolge erfolgen

236
Optionsnummer
53
Dezember
3
Option 53, die den DHCP-Pakettyp 5 – DHCPACK definiert

 
Optionslänge
1
Dezember
1

 
Optionswert
5
Dezember
1

 
Optionsnummer
1
Dezember
1
Option, dem DHCP-Client eine Netzwerkmaske anzubieten

 
Optionslänge
4
Dezember
1

 
Optionswert
255.255.224.0
String
4

 
Optionsnummer
3
Dezember
1
Option, dem DHCP-Client ein Standard-Gateway anzubieten

 
Optionslänge
4
Dezember
1

 
Optionswert
172.16.12.1
String
4

 
Optionsnummer
6
Dezember
1
Option zum Anbieten von DHCP für den DNS-Client

 
Optionslänge
4
Dezember
1

 
Optionswert
8.8.8.8
String
4

 
Optionsnummer
51
Dezember
1
Die Lebensdauer der ausgegebenen Netzwerkparameter in Sekunden, nach deren Ablauf der DHCP-Client sie erneut anfordern muss

 
Optionslänge
4
Dezember
1

 
Optionswert
86400
Dezember
4

 
Optionsnummer
82
Dezember
1
Option 82 wiederholt, was in DHCPDISCOVER kam

 
Optionslänge
18
Dezember
1

 
Optionswert
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dezember
18

 
Ende des Pakets
255
Dezember
1
255 symbolisiert das Ende des Pakets

Einstellung

Die Installation besteht eigentlich aus der Installation der für die Arbeit notwendigen Python-Module. Es wird davon ausgegangen, dass MySQL bereits installiert und konfiguriert ist.

FreeBSD

pkg install python3 python3 -m securepip 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 den pydhcp.sql-Dump hinein und konfigurieren die Konfigurationsdatei.

Konfiguration

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

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 prüfen prüfen pydhcp option_8.8.8.8_hex:sw_port82:1:20 option_22_hex:sw_port82:2:16 option_18_hex:sw_mac:82:26 40 Wählen Sie IP, Maske, Router, DNS von Benutzern aus, wobei Upper(mac)=upper('{option_3_AgentRemoteId_hex}') und Upper(port)=upper('{option_1_AgentCircuitId_port_hex}') gelten. Wählen Sie IP, Maske, Router, DNS von Benutzern aus, wobei Upper(mac)=upper('{sw_mac}') und Upper(port)=upper('{sw_port82}') gelten. Wählen Sie IP, Maske, Router, DNS von Benutzern aus, wobei Upper(Mac)=Upper('{ClientMacAddress}') ist. In den Verlauf (id,dt,mac,ip,comment) Werte einfügen (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')

Nun ausführlicher zu den Tags:

Der Abschnitt dhcpserver beschreibt die Grundeinstellungen zum Starten des Servers, nämlich:

  • Host – welche IP-Adresse der Server auf Port 67 überwacht
  • Broadcast – welche IP ist die Broadcast-Adresse für DHCPOFFER und DHCPACK?
  • DHCPServer – wie lautet die IP des DHCP-Servers?
  • LeaseTime Lease-Zeit der vergebenen IP-Adresse
  • ThreadLimit – wie viele Threads gleichzeitig laufen, um eingehende UDP-Pakete auf Port 67 zu verarbeiten. Es soll bei Projekten mit hoher Auslastung helfen 😉
  • defaultMask,defaultRouter,defaultDNS – was dem Abonnenten standardmäßig angeboten wird, wenn eine IP in der Datenbank gefunden wird, aber keine zusätzlichen Parameter dafür angegeben sind

MySQL-Abschnitt:

Host, Benutzername, Passwort, Basisname – alles spricht für sich. Eine ungefähre Datenbankstruktur wird auf veröffentlicht GitHub

Abfrageabschnitt: Anfragen zum Empfang von OFFER/ACK werden hier beschrieben:

  • offer_count – die Anzahl der Zeilen mit Anfragen, die ein Ergebnis wie „ip,mask,router,dns“ zurückgeben
  • offer_n – Abfragezeichenfolge. Wenn return leer ist, wird die folgende Angebotsanforderung ausgeführt
  • History_sql – eine Abfrage, die beispielsweise in die „Autorisierungshistorie“ für einen Abonnenten schreibt

Anfragen können beliebige Variablen aus dem Optionsbereich oder Optionen aus dem DHCP-Protokoll enthalten.

Abschnitt „Optionen“. Hier wird es interessanter. Hier können wir Variablen erstellen, die wir später im Abfrageabschnitt verwenden können.

Zum Beispiel:

option_82_hex:sw_port1:20:22

, diese Befehlszeile nimmt die gesamte Zeile, die in der DHCP-Anfrageoption 82 kam, im Hex-Format, im Bereich von 20 bis einschließlich 22 Bytes und fügt sie in die neue Variable sw_port1 ein (Switch-Port, von dem die Anfrage kam)

option_82_hex:sw_mac:26:40

, definieren Sie die Variable sw_mac und übernehmen Sie dabei den Hexadezimalwert aus dem Bereich 26:40

Sie können alle möglichen Optionen sehen, die in Abfragen verwendet werden können, indem Sie den Server mit dem Schalter -d starten. Wir werden so etwas sehen:

--ein DHCPINFORM-Paket kam auf Port 67 an, von 0025224ad764 , b'x91xa5xe0xa3xa5xa9-x8fx8a' , ('172.30.114.25', 68) {'ClientMacAddress': '0025224ad764', 'ClientMacAddressByte': b'x00 7%"Jxd 91d' , ' 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', 'option12': 53, 'option53': 55, 'option55': 60, 'option60': 61, 'option61': 82, 'option82': 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'", 'result': False, 'secs': 001, 'siaddr': '589', 'sw_mac': '2e1eb06ad', 'sw_port89': '8', 'xidbyte': b'

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

Lassen Sie uns für den Verlauf aufzeichnen, dass der Client die IP-Adresse erhalten hat:

DHCP+MySQL-Server in Python

DHCP+MySQL-Server in Python

Serverstart

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

— d Konsolenausgabemodus DEBUG
- c <Dateiname> Konfigurationsdatei

Nachbesprechung

Und nun weitere Details zur Implementierung des Servers in Python. Es ist ein Schmerz. Python wurde spontan gelernt. Viele Momente sind im Stil von „Wow, irgendwie habe ich es geschafft“ gestaltet. Überhaupt nicht optimiert und hauptsächlich aufgrund der geringen Erfahrung in der Python-Entwicklung in dieser Form belassen. Ich werde auf die interessantesten Aspekte der Serverimplementierung im „Code“ eingehen.

Parser für XML-Konfigurationsdateien

Es wird das Standard-Python-Modul xml.dom verwendet. Es scheint einfach, aber während der Implementierung gab es einen spürbaren Mangel an klarer Dokumentation und Beispielen im Netzwerk, in denen dieses Modul verwendet wurde.

    tree = minidom.parse(gconfig["config_file"]) mconfig=tree.getElementsByTagName("mysql") für 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") für 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") für elem in qconfig: gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data für 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") für elem in Optionen: node=elem.getElementsByTagName("option") für Optionen in node : optionsMod.append(options.firstChild.data)

Multithreading

Seltsamerweise ist Multithreading in Python sehr klar und einfach implementiert.

def PacketWork(data,addr): ... # Implementierung des Parsens des eingehenden Pakets und der Reaktion darauf ... while True: data, addr = udp_socket.recvfrom(1024) # Warten auf das UDP-Paket thread = threading.Thread( target=PacketWork , args=(data,addr,)).start() # wie es kam – wir starten die zuvor definierte PacketWork-Funktion im Hintergrund mit Parametern während threading.active_count() >gconfig["dhcp_ThreadLimit"]: Zeit. sleep(1) # wenn die Zahl Es laufen bereits mehr Threads als in den Einstellungen, wir warten bis weniger davon vorhanden sind

DHCP-Paket empfangen/senden

Um über die Netzwerkkarte eingehende UDP-Pakete abzufangen, müssen Sie den Socket „erhöhen“:

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

, wobei die Flags sind:

  • AF_INET – bedeutet, dass das Adressformat IP: Port ist. Es kann auch AF_UNIX geben – wobei die Adresse durch den Dateinamen angegeben wird.
  • SOCK_DGRAM – bedeutet, dass wir kein „Rohpaket“ akzeptieren, sondern eines, das die Firewall bereits passiert hat, und ein teilweise beschnittenes Paket. Diese. Wir erhalten nur ein UDP-Paket ohne die „physische“ Komponente des UDP-Paket-Wrappers. Wenn Sie das SOCK_RAW-Flag verwenden, müssen Sie auch diesen „Wrapper“ analysieren.

Das Senden eines Pakets kann wie eine Rundsendung sein:

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

, und an die Adresse „woher das Paket kam“:

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # Schalten Sie den Socket in den Multi-Listener-Modus um rz=udp_socket.sendto(packetack, addr)

, wobei SOL_SOCKET die „Protokollebene“ zum Einstellen von Optionen bedeutet,

, SO_BROADCAST Option, dass das Helmpaket „broadcast“ wird

  Die Option ,SO_REUSEADDR schaltet den Socket in den Modus „Viele Listener“. Theoretisch ist es in diesem Fall unnötig, aber auf einem der FreeBSD-Server, auf denen ich getestet habe, funktionierte der Code ohne diese Option nicht.

Parsen eines DHCP-Pakets

Hier gefiel mir Python besonders gut. Es stellt sich heraus, dass Sie sofort mit dem Bytecode recht flexibel umgehen können. Dies ermöglicht eine sehr einfache Übersetzung in Dezimalwerte, Zeichenfolgen und Hexadezimalwerte, d. h. Das ist es, was wir tatsächlich brauchen, um die Struktur des Pakets zu verstehen. So können Sie beispielsweise einen Bereich von Bytes in HEX und nur Bytes erhalten:

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

Packen Sie die Bytes in eine Struktur:

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

IP aus Struktur abrufen:

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

Umgekehrt:

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

Das ist vorerst alles 😉

Source: habr.com

Kommentar hinzufügen