DHCP + MySQL-Server in Python



Der Zweck dieses Projekts war:

  • Lernen von DHCP über ein IPv4-Netzwerk
  • Python lernen (etwas mehr als von Grund auf neu;))
  • Ersetzen des DB2DHCP- Servers (meine Gabel), des Originals hier , der unter dem neuen Betriebssystem immer schwieriger zusammenzubauen ist. Ja, und ich mag das nicht, die Binärdatei, die "momentan nicht ändern" kann.
  • Erhalten eines funktionierenden DHCP-Servers mit der Möglichkeit, die IP-Adresse des Teilnehmers über den Mac des Teilnehmers oder eine Reihe von Mac-Switches + Port auszuwählen (Option 82)
  • ein anderes Fahrrad schreiben (Oh! das ist mein Lieblingsbeschäftigung)
  • Lyuli über seinen Blick auf Habrahabr informieren (oder besser einladen);)

Ergebnis: es funktioniert;) Getestet unter FreeBSD und Ubuntu OS. Theoretisch kann der Code aufgefordert werden, unter jedem Betriebssystem zu arbeiten, weil Der Code enthält keine spezifischen Bindungen.
Achtung Viel weiter.

Link zum Repository, damit Fans "lebendig berühren" können.

Der Prozess der Installation, Konfiguration und Verwendung des Ergebnisses der „Untersuchung der Hardware“ ist viel geringer und dann eine kleine Theorie zum DHCP-Protokoll. Für sich. Und für die Geschichte;)

Ein bisschen Theorie


Was ist DHCP?


Dies ist ein Netzwerkprotokoll, mit dem ein Gerät seine IP-Adresse (also andere Parameter wie ein Gateway, DNS usw.) von einem DHCP-Server herausfinden kann. Pakete werden über UDP ausgetauscht. Das allgemeine Funktionsprinzip des Geräts beim Anfordern von Netzwerkparametern lautet wie folgt:

  1. Das Gerät (Client) sendet eine UDP-Broadcast-Anfrage (DHCPDISCOVER) über das Netzwerk mit der Anfrage: "Nun, jemand, geben Sie mir eine IP-Adresse." Normalerweise (aber nicht immer) stammt die Anforderung von Port 68 (Quelle) und das Ziel von Port 67 (Ziel). Einige Geräte senden auch Pakete von Port 67. Im DHCPDISCOVER-Paket ist die MAC-Adresse des Clientgeräts enthalten.
  2. Alle im Netzwerk befindlichen DHCP-Server (und möglicherweise mehrere) bilden ein Formular für das Gerät, das DHCPDISCOVER einen DHCPOFFER-Satz mit Netzwerkeinstellungen gesendet hat, und sendet ihn auch über das Netzwerk. Die Identifizierung, für die dieses Paket bestimmt ist, erfolgt an die MAC-Adresse des Clients, die zuvor in der DHCPDISCOVER-Anforderung angegeben wurde.
  3. Der Client empfängt Pakete mit Angeboten für Netzwerkeinstellungen, wählt die attraktivsten aus (die Kriterien können sich beispielsweise nach dem Zeitpunkt der Paketzustellung und der Anzahl der Zwischenrouten unterscheiden) und veranlasst den DHCP-Server, DHCPREQUEST mit Netzwerkeinstellungen offiziell anzufordern. In diesem Fall geht das Paket an einen bestimmten DHCP-Server.
  4. Der Server, der DHCPREQUEST empfangen hat, sendet ein DHCPACK-Paket, in dem die Netzwerkeinstellungen für diesen Client erneut aufgelistet werden



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

Paketformat



Im Allgemeinen sieht der Ethernet-Paketrahmen ungefähr so ​​aus:



In unserem Fall werden wir nur Daten direkt aus dem Inhalt des UDP-Pakets ohne die OSI-Schichtprotokoll-Header berücksichtigen, nämlich die DHCP-Struktur:

DHCPDISCOVER


Der Prozess zum Abrufen der IP-Adresse für das Gerät beginnt also mit der Tatsache, dass der DHCP-Client eine Broadcast-Anforderung von Port 68 an 255.255.255.255:67 sendet. In diesem Paket enthält der Client seine MAC-Adresse sowie das, was er vom DHCP-Server empfangen möchte. Die Struktur des Pakets ist in der folgenden Tabelle beschrieben.

DHCPDISCOVER-Paketstrukturtabelle
PaketpositionWert NameBeispielEinreichungByteErklärung
1Startanforderung1Hex1Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client
2Hardwaretyp1Hex1Art der Hardwareadresse in diesem Protokoll 1 - MAC
3Hardware passt Länge6Hex1MAC-Adresslänge des Geräts
4Hopfen1Hex1Anzahl der Zwischenstrecken
5Transaktions-ID23: cf: de: 1dHex4Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert
7Zweite verstrichen0Hex4Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse
9Bootp-Flags0Hex2Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können
11Client-IP-Adresse0.0.0.0String4Client-IP-Adresse (falls vorhanden)
15Ihre Client-IP-Adresse0.0.0.0String4Vom Server vorgeschlagene IP-Adresse (falls vorhanden)
19Nächste Server-IP-Adresse0.0.0.0String4Server-IP-Adresse (falls bekannt)
23IP-Adresse des Relay-Agenten172.16.114.41String4IP-Adresse des Relay-Agenten (z. B. ein Switch)
27Client-MAC-Adresse14: d6: 4d: a7: c9: 55Hex6MAC-Adresse des Absenders des Pakets (Client)
31Auffüllen der Client-Hardware-AdresseHex10Reservierter Platz. Normalerweise Nullen
41Server-HostnameString64Der Name des DHCP-Servers. Normalerweise nicht übertragen
105Name der StartdateiString128Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird
235Magischer Keks63: 82: 53: 63Hex4Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen
236Optionsnummer53Dez.1Option 53, die den Typ des DHCP-Pakets angibt

1 - DHCPDISCOVER
3 - DHCPREQUEST
2 - DHCPOFFER
5 - DHCPACK
8 - DHCPINFORM
Optionslänge1Dez.1
Optionswert1Dez.1
Optionsnummer50Dez.1Welche IP-Adresse möchte der Client erhalten?
Optionslänge4Dez.1
Optionswert172.16.134.61String4
Optionsnummer551Die vom Client angeforderten Netzwerkparameter. Die Zusammensetzung kann unterschiedlich sein

01 - Netzmaske
03 - Gateway
06 - DNS
oc - Hostname
0f - Netzwerkdomänenname
1c - Adresse der Broadcast-Anfrage (Broadcast)
42 - TFTP-Servername
79 - Klassenlose statische Route
Optionslänge81
Optionswert01: 03: 06: 0c: 0f: 1c: 42: 798
Optionsnummer82Dez.Option 82, bei der die MAC-Adresse des Repeater-Geräts und einige zusätzliche Werte übertragen werden.

Meistens der Switch-Port, auf dem der DHCP-Endclient ausgeführt wird. Zusätzliche Optionen sind in diese Option „eingebettet“. Das erste Byte ist die Nummer der „Unteroption“, das zweite die Länge und dann der 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 Anforderung stammt

Agent Remote ID = 00: 06: c8: be: 19: 93: 11: 48 - MAC-Adresse des DHCP-Relay-Geräts
Optionslänge18Dez.
Optionswert01.06
00: 04: 00: 01: 00: 04
02.08
00: 06: c8: be: 19: 93: 11: 48
Hex
Ende des Pakets255Dez.1255 symbolisiert das Ende des Pakets


DHCPOFFER


Sobald der Server das DHCPDISCOVER-Paket empfängt und sieht, dass er dem Client etwas von der angeforderten anbieten kann, bildet er eine Antwort dafür - DHCPOFFER. Die Antwort wird an den Port "von wo sie kam" gesendet, Broadcast, weil Derzeit verfügt der Client noch nicht über eine IP-Adresse. Daher kann er ein Paket nur empfangen, wenn es gesendet wird. Der Client erkennt, dass es sich um ein Paket von MAC handelt, an seiner Adresse innerhalb des Pakets sowie an der Transaktionsnummer, die er zum Zeitpunkt der Erstellung des ersten Pakets generiert.

DHCPOFFER-Paketstrukturtabelle
PaketpositionName des Wertes (allgemein)BeispielEinreichungByteErklärung
1Startanforderung1Hex1Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client
2Hardwaretyp1Hex1Art der Hardwareadresse in diesem Protokoll 1 - MAC
3Hardware passt Länge6Hex1MAC-Adresslänge des Geräts
4Hopfen1Hex1Anzahl der Zwischenstrecken
5Transaktions-ID23: cf: de: 1dHex4Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert
7Zweite verstrichen0Hex4Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse
9Bootp-Flags0Hex2Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können. In diesem Fall bedeutet 0 den Unicast-Anforderungstyp
11Client-IP-Adresse0.0.0.0String4Client-IP-Adresse (falls vorhanden)
15Ihre Client-IP-Adresse172.16.134.61String4Vom Server vorgeschlagene IP-Adresse (falls vorhanden)
19Nächste Server-IP-Adresse0.0.0.0String4Server-IP-Adresse (falls bekannt)
23IP-Adresse des Relay-Agenten172.16.114.41String4IP-Adresse des Relay-Agenten (z. B. ein Switch)
27Client-MAC-Adresse14: d6: 4d: a7: c9: 55Hex6MAC-Adresse des Absenders des Pakets (Client)
31Auffüllen der Client-Hardware-AdresseHex10Reservierter Platz. Normalerweise Nullen
41Server-HostnameString64Der Name des DHCP-Servers. Normalerweise nicht übertragen
105Name der StartdateiString128Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird
235Magischer Keks63: 82: 53: 63Hex4Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen
236Optionsnummer53Dez.1Option 53, die den Typ des DHCP 2-Pakets angibt - DHCPOFFER
Optionslänge1Dez.1
Optionswert2Dez.1
Optionsnummer1Dez.1Option mit DHCP-Client-Netzwerkmaske
Optionslänge4Dez.1
Optionswert255.255.224.0String4
Optionsnummer3Dez.1Option mit Standard-Gateway für DHCP-Clients
Optionslänge4Dez.1
Optionswert172.16.12.1String4
Optionsnummer6Dez.1Option, die dem DNS-Client DHCP anbietet
Optionslänge4Dez.1
Optionswert8.8.8.8String4
Optionsnummer51Dez.1Die Lebensdauer der ausgegebenen Netzwerkparameter in Sekunden. Danach muss der DHCP-Client sie erneut anfordern
Optionslänge4Dez.1
Optionswert86400Dez.4
Optionsnummer82Dez.1Option 82 wiederholt, was in DHCPDISCOVER enthalten war
Optionslänge18Dez.1
Optionswert01: 08: 00: 06: 00
01: 01: 00: 00: 01
02: 06: 00: 03: 0f
26: 4d: ec
Dez.18
Ende des Pakets255Dez.1255 symbolisiert das Ende des Pakets


DHCPREQUEST


Nachdem der Client DHCPOFFER empfangen hat, bildet er ein Paket mit einer Anforderung für Netzwerkparameter nicht an alle DHCP-Server im Netzwerk, sondern nur an einen bestimmten, dessen DHCPOFFER-Vorschlag ihm am besten gefallen hat. Die Kriterien für "Gefällt mir" können unterschiedlich sein und hängen von der Implementierung des DHCP-Clients ab. Der Empfänger der Anforderung wird anhand der MAC-Adresse des DHCP-Servers angegeben. Außerdem kann das DHCPREQUEST-Paket vom Client gesendet werden, ohne zuvor DHCPDISCOVER zu bilden, wenn die IP-Adresse des Servers zuvor empfangen wurde.

DHCPREQUEST-Paketstrukturtabelle
PaketpositionName des Wertes (allgemein)BeispielEinreichungByteErklärung
1Startanforderung1Hex1Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client
2Hardwaretyp1Hex1Art der Hardwareadresse in diesem Protokoll 1 - MAC
3Hardware passt Länge6Hex1MAC-Adresslänge des Geräts
4Hopfen1Hex1Anzahl der Zwischenstrecken
5Transaktions-ID23: cf: de: 1dHex4Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert
7Zweite verstrichen0Hex4Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse
9Bootp-Flags8000Hex2Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können. In diesem Fall "Broadcast"
11Client-IP-Adresse0.0.0.0String4Client-IP-Adresse (falls vorhanden)
15Ihre Client-IP-Adresse172.16.134.61String4Vom Server vorgeschlagene IP-Adresse (falls vorhanden)
19Nächste Server-IP-Adresse0.0.0.0String4Server-IP-Adresse (falls bekannt)
23IP-Adresse des Relay-Agenten172.16.114.41String4IP-Adresse des Relay-Agenten (z. B. ein Switch)
27Client-MAC-Adresse14: d6: 4d: a7: c9: 55Hex6MAC-Adresse des Absenders des Pakets (Client)
31Auffüllen der Client-Hardware-AdresseHex10Reservierter Platz. Normalerweise Nullen
41Server-HostnameString64Der Name des DHCP-Servers. Normalerweise nicht übertragen
105Name der StartdateiString128Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird
235Magischer Keks63: 82: 53: 63Hex4Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen
236Optionsnummer53Dez.3Option 53, die den Typ des DHCP 3-Pakets angibt - DHCPREQUEST
Optionslänge1Dez.1
Optionswert3Dez.1
Optionsnummer61Dez.1Client-ID: 01 (für Ehernet) + MAC-Adresse des Clients
Optionslänge7Dez.1
Optionswert01: 2c: ab: 25: ff: 72: a6Hex7
Optionsnummer60Dez."Herstellerklassen-ID." In meinem Fall wird die Version des DHCP-Clients gemeldet. Vielleicht geben andere Geräte etwas anderes zurück. Windows meldet beispielsweise MSFT 5.0
Optionslänge11Dez.
Optionswertudhcp 0.9.8String
Optionsnummer551Die vom Client angeforderten Netzwerkparameter. Die Zusammensetzung kann unterschiedlich sein

01 - Netzmaske
03 - Gateway
06 - DNS
oc - Hostname
0f - Netzwerkdomänenname
1c - Adresse der Broadcast-Anfrage (Broadcast)
42 - TFTP-Servername
79 - Klassenlose statische Route
Optionslänge81
Optionswert01: 03: 06: 0c: 0f: 1c: 42: 798
Optionsnummer82Dez.1Option 82 wiederholt, was in DHCPDISCOVER enthalten war
Optionslänge18Dez.1
Optionswert01: 08: 00: 06: 00
01: 01: 00: 00: 01
02: 06: 00: 03: 0f
26: 4d: ec
Dez.18
Ende des Pakets255Dez.1255 symbolisiert das Ende des Pakets


DHCPACK


Als Bestätigung für die Tatsache, dass "Ja, es ist Ihre IP-Adresse und ich werde sie niemand anderem geben" vom DHCP-Server, gibt es ein Paket im DHCPACK-Format vom Server zum Client. Dies entspricht den übrigen gesendeten Paketen. Obwohl ich im folgenden Code des in Python implementierten DHCP-Servers für alle Fälle jede Broadcast-Anforderung dupliziere, indem ich ein Paket an eine bestimmte Client-IP sende, sofern dies bereits bekannt ist. Darüber hinaus ist es dem DHCP-Server überhaupt egal, ob das DHCPACK-Paket den Client erreicht hat. Wenn der Client kein DHCPACK empfängt, wiederholt er nach einer Weile einfach DHCPREQUEST

DHCPACK-Paketstrukturtabelle
PaketpositionName des Wertes (allgemein)BeispielEinreichungByteErklärung
1Startanforderung2Hex1Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client
2Hardwaretyp1Hex1Art der Hardwareadresse in diesem Protokoll 1 - MAC
3Hardware passt Länge6Hex1MAC-Adresslänge des Geräts
4Hopfen1Hex1Anzahl der Zwischenstrecken
5Transaktions-ID23: cf: de: 1dHex4Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert
7Zweite verstrichen0Hex4Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse
9Bootp-Flags8000Hex2Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können. In diesem Fall "Broadcast"
11Client-IP-Adresse0.0.0.0String4Client-IP-Adresse (falls vorhanden)
15Ihre Client-IP-Adresse172.16.134.61String4Vom Server vorgeschlagene IP-Adresse (falls vorhanden)
19Nächste Server-IP-Adresse0.0.0.0String4Server-IP-Adresse (falls bekannt)
23IP-Adresse des Relay-Agenten172.16.114.41String4IP-Adresse des Relay-Agenten (z. B. ein Switch)
27Client-MAC-Adresse14: d6: 4d: a7: c9: 55Hex6MAC-Adresse des Absenders des Pakets (Client)
31Auffüllen der Client-Hardware-AdresseHex10Reservierter Platz. Normalerweise Nullen
41Server-HostnameString64Der Name des DHCP-Servers. Normalerweise nicht übertragen
105Name der StartdateiString128Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird
235Magischer Keks63: 82: 53: 63Hex4Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen
236Optionsnummer53Dez.3Option 53 zur Angabe des DHCP-Pakettyps 5 - DHCPACK
Optionslänge1Dez.1
Optionswert5Dez.1
Optionsnummer1Dez.1Option mit DHCP-Client-Netzwerkmaske
Optionslänge4Dez.1
Optionswert255.255.224.0String4
Optionsnummer3Dez.1Option mit Standard-Gateway für DHCP-Clients
Optionslänge4Dez.1
Optionswert172.16.12.1String4
Optionsnummer6Dez.1Option, die dem DNS-Client DHCP anbietet
Optionslänge4Dez.1
Optionswert8.8.8.8String4
Optionsnummer51Dez.1Die Lebensdauer der ausgegebenen Netzwerkparameter in Sekunden. Danach muss der DHCP-Client sie erneut anfordern
Optionslänge4Dez.1
Optionswert86400Dez.4
Optionsnummer82Dez.1Option 82 wiederholt, was in DHCPDISCOVER enthalten war
Optionslänge18Dez.1
Optionswert01: 08: 00: 06: 00
01: 01: 00: 00: 01
02: 06: 00: 03: 0f
26: 4d: ec
Dez.18
Ende des Pakets255Dez.1255 symbolisiert das Ende des Pakets



Installation


Die Installation besteht tatsächlich aus der Installation der für den Job erforderlichen Python-Module. Es wird davon ausgegangen, dass MySQL bereits installiert und konfiguriert ist.

Freebsd


  pkg installiere python3
 python3 -m surepip
 pip3 installiert mysql-connector 

Ubuntu


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

Wir erstellen die MySQL-Datenbank, füllen den Speicherauszug pydhcp.sql aus und konfigurieren die Konfigurationsdatei.

Konfiguration


Alle Servereinstellungen sind im XML-Dateiformat. Referenzdatei:

  <? xml version = "1.0"?>
 <config>
     <dhcpserver>
	 <host> 0.0.0.0 </ host>
         <broadcast> 255.255.255.255 </ broadcast>
         <DHCPServer> 192.168.0.71 </ DHCPServer>
	 <LeaseTime> 8600 </ LeaseTime>
	 <ThreadLimit> 1 </ ThreadLimit>
         <defaultMask> 255.255.255.0 </ defaultMask>
         <defaultRouter> 192.168.0.1 </ defaultRouter>
         <defaultDNS> 8.8.8.8 </ defaultDNS>
     </ dhcpserver>
     <mysql>
         <host> localhost </ host>
	 <Benutzername> Test </ Benutzername>
	 <Kennwort> Test </ Kennwort>
	 <Basename> pydhcp </ Basisname>
     </ myql>
     <Optionen>
        <option> option_82_hex: sw_port1: 20:22 </ option>       
        <option> option_82_hex: sw_port2: 16:18 </ option>       
        <option> option_82_hex: sw_mac: 26: 40 </ option>
     </ options>    
     <Anfrage>
         <Angebotsanzahl> 3 </ Angebotsanzahl>
	 <offer_1> Wählen Sie IP, Maske, Router und DNS von Benutzern aus, bei denen Upper (Mac) = Upper ('{option_82_AgentRemoteId_hex}') und Upper (Port) = Upper ('{Option_82_AgentCircuitId_port_hex}') </offer_1>
         <offer_2> Wählen Sie IP, Maske, Router und DNS von Benutzern aus, bei denen Upper (Mac) = Upper ('{sw_mac}') und Upper (Port) = Upper ('{sw_port2}') </offer_2>
         <offer_3> Wählen Sie IP, Maske, Router und DNS von Benutzern aus, bei denen Upper (Mac) = Upper ('{ClientMacAddress}') </offer_3> ist
	 <history_sql> Werte in den Verlauf (id, dt, mac, ip, comment) einfügen (null, now (), '{ClientMacAddress}', '{RequestedIpAddress}', 'DHCPACK / INFORM') </ history_sql>
     </ query>
 </ config> 

Jetzt detaillierter zu Tags:

Der Abschnitt dhcpserver beschreibt die Grundeinstellungen zum Starten des Servers, nämlich:
  • Host - Welche IP-Adresse überwacht der Server an Port 67?
  • Broadcast - Welche IP ist eine Broadcast für DHCPOFFER und DHCPACK?
  • DHCPServer - Wie lautet die IP-Adresse des DHCP-Servers?
  • LeaseTime Lease-Zeit der ausgegebenen IP-Adresse
  • ThreadLimit - wie viele Threads gleichzeitig ausgeführt werden, um eingehende UDP-Pakete auf Port 67 zu verarbeiten. Es wird davon ausgegangen, dass dies bei hoch geladenen Projekten hilfreich ist;)
  • defaultMask, defaultRouter, defaultDNS - Was wird dem Abonnenten standardmäßig angeboten, wenn IP in der Datenbank gefunden wird, aber keine zusätzlichen Parameter für ihn angegeben sind

MySQL-Abschnitt:

Host, Benutzername, Passwort, Basisname - alles spricht für sich. Auf GitHub veröffentlichte Beispieldatenbankstruktur

Abfrageabschnitt: In diesem Abschnitt werden die Anforderungen für ANGEBOT / ACK beschrieben:

  • Offer_Count - Die Anzahl der Zeilen mit Anforderungen, die ein Ergebnis der Form IP, Maske, Router, DNS zurückgeben
  • Offer_n ist die Abfragezeichenfolge. Wenn die Rücksendung leer ist, wird die folgende Angebotsanforderung ausgeführt
  • history_sql - Schreibanforderung zum Beispiel an den "Autorisierungsverlauf" durch den Abonnenten

Alle Variablen aus dem Optionsbereich oder Optionen aus dem DHCP-Protokoll können an Anforderungen teilnehmen.

Abschnittsoptionen. Hier ist es schon 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 Zeile ist der Befehl, die gesamte Zeile, die in der DHCP-Anforderung von Option 82 eingegangen ist, im Hex-Format im Bereich von 20 bis einschließlich 22 Byte zu übernehmen und in die neue Variable sw_port1 (den Switch-Port, von dem die Anforderung stammt) einzufügen.

 option_82_hex:sw_mac:26:40 

Definieren Sie die Variable sw_mac und nehmen Sie hex aus dem Bereich 26:40

Sie können alle möglichen Optionen anzeigen, die in Abfragen verwendet werden können, indem Sie den Server mit der Option -d starten. Wir werden so etwas wie dieses Protokoll sehen:

  - Das DHCPINFORM-Paket kam an Port 67 von 0025224ad764, b '\ x91 \ xa5 \ xe0 \ xa3 \ xa5 \ xa9- \ x8f \ x8a' ('172.30.114.25', 68).
 {'ClientMacAddress': '0025224ad764',
  'ClientMacAddressByte': b '\ x00% "J \ xd7d',
  'HType': 'Ethernet',
  'Hostname': b '\ x91 \ xa5 \ xe0 \ xa3 \ xa5 \ xa9- \ x8f \ x8a',
  'ReqListDNS': Richtig,
  'ReqListDomainName': Richtig,
  'ReqListPerfowmRouterDiscover': True,
  'ReqListRouter': Richtig,
  'ReqListStaticRoute': Richtig,
  'ReqListSubnetMask': Richtig,
  'ReqListVendorSpecInfo': 43,
  'RequestedIpAddress': '0.0.0.0',
  'Anbieter': b'MSFT 5.0 ',
  'chaddr': '0025224ad764',
  'ciaddr': '172.30.128.13',
  'flags': b '\ x00 \ x00',
  'giaddr': '172.30.114.25',
  'gpoz': 308,
  'hlen': 6,
  'Hopfen': 1,
  'htype': 'MAC',
  'magic_cookie': b'c \ x82Sc ',
  'op': 'DHCPINFORM',
  'Option12': 12,
  'option53': 53,
  'option55': 55,
  'option60': 60,
  'option61': 61,
  'option82': 82,
  'option_82_byte': b '\ x12 \ x01 \ x06 \ x00 \ x04 \ x00 \ x01 \ x00 \ x06 \ x02 \ x08 \ x00'
                    b '\ x06 \ x00 \ x1eX \ x9e \ xb2 \ xad',
  'option_82_hex': '12010600040001000602080006001e589eb2ad',
  'option_82_len': 18,
  'option_82_str': "b '\\ x12 \\ x01 \\ x06 \\ x00 \\ x04 \\ x00 \\ x01 \\ x00 \\ x06 \\ x02 \\ x08 \\ x00 \\ x06 \\ x00 \ \ x1eX \\ x9e \\ xb2 \\ xad '",
  'Ergebnis': Falsch,
  'secs': 768,
  'siaddr': '0.0.0.0',
  'sw_mac': '001e589eb2ad',
  'sw_port1': '06',
  'xidbyte': b '<\ x89} \ x8c',
  'xidhex': '3c897d8c',
  'yiaddr': '0.0.0.0'} 

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

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





Serverstart


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

- d Ausgabemodus an die DEBUG-Konsole
- c Konfigurationsdatei <Dateiname>

Nachbesprechung


Und jetzt mehr zur Implementierung des Servers in Python. Das ist ein Schmerz. Python wurde im laufenden Betrieb untersucht. Viele Momente entstehen im Stil von: "Wow, ich habe irgendwie getan, was funktioniert." Überhaupt nicht optimiert und in dieser Form hauptsächlich aufgrund der geringen Erfahrung mit der Entwicklung in Python belassen. Ich werde auf die interessantesten Momente der Serverimplementierung im "Code" eingehen.

Parser für XML-Konfigurationsdateien


Das Standard-Python-Modul xml.dom wird verwendet. Es scheint einfach zu sein, aber während der Implementierung gab es einen spürbaren Mangel an vernünftiger Dokumentation und Beispielen im Netzwerk, das dieses Modul verwendet.

  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 ("Abfrage")
     für elem in qconfig:  
         gconfig ["Offer_Count"] = elem.getElementsByTagName ("Offer_Count") [0] .firstChild.data                          
         für num in range (int (gconfig ["quote_count"])):
             gconfig ["Angebot _" + str (num + 1)] = elem.getElementsByTagName ("Angebot _" + str (num + 1)) [0] .firstChild.data      
         gconfig ["history_sql"] = elem.getElementsByTagName ("history_sql") [0] .firstChild.data                          
     options = tree.getElementsByTagName ("options")       
     für elem in options:          
         node = elem.getElementsByTagName ("Option")
         für Optionen im Knoten:
             optionsMod.append (options.firstChild.data) 

Multithreading


Seltsamerweise ist Multithreading in Python sehr klar und einfach implementiert.

  def PacketWork (Daten, Adr): 
 ...
 # Implementierung der Analyse des empfangenen Pakets und der Antwort darauf
 ...


 während wahr:
     data, addr = udp_socket.recvfrom (1024) # auf UDP-Paket warten
     thread = threading.Thread (target = PacketWork, args = (data, addr,)). start () # wie es kam - führen Sie die zuvor im Hintergrund definierte PacketWork-Funktion mit Parametern aus
     während threading.active_count ()> gconfig ["dhcp_ThreadLimit"]:
        time.sleep (1) # Wenn die Anzahl der bereits ausgeführten Threads größer ist als in den Einstellungen, warten Sie, bis sie kleiner werden 


DHCP-Paket empfangen / senden


Um UDP-Pakete abzufangen, die über die Netzwerkkarte eingehen, 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)) 

wo die Flaggen sind:

  • AF_INET - bedeutet, dass das Adressformat IP: port ist. Vielleicht AF_UNIX - wobei die Adresse durch den Dateinamen angegeben wird.
  • SOCK_DGRAM - bedeutet, dass wir kein "Rohpaket" akzeptieren, sondern bereits eine Firewall durchlaufen und das Paket teilweise abgeschnitten haben. Das heißt, Wir erhalten nur das 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 Sendung sein:

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

und an die Adresse "Woher kam das Paket":
  udp_socket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Schalten Sie den Socket in den Modus "Viele Listener"
                         rz = udp_socket.sendto (Paketpaket, Adr) 

wobei SOL_SOCKET "Protokollebene" zum Einstellen von Optionen bedeutet,

Die SO_BROADCAST-Option ist das Broadcast-Helmpaket

Die Option SO_REUSEADDR schaltet den Socket in den Multi-Listener-Modus. Theoretisch ist dies in diesem Fall nicht erforderlich, aber auf einem der von mir getesteten FreeBSD-Server funktionierte der Code ohne diese Option nicht.

Analyse von DHCP-Paketen


Hier hat mir Python sehr gut gefallen. Es stellt sich heraus, dass Sie mit dem Bytecode ganz einfach umgehen können. Ermöglichen, dass es sehr einfach ist, in Dezimalwerte, Zeichenfolgen und Hexadezimalwerte zu übersetzen - d. H. Was wir eigentlich brauchen, um die Struktur des Pakets zu verstehen. So können Sie beispielsweise eine Reihe von Bytes in HEX und nur Bytes abrufen:

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

, Bytes in eine Struktur packen:

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

IP aus der Struktur abrufen:

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


Umgekehrt:

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

Das ist alles;)

Source: https://habr.com/ru/post/de445338/


All Articles