
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:
- 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.
- 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.
- 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.
- 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-PaketstrukturtabellePaketposition | Wert Name | Beispiel | Einreichung | Byte | Erklärung |
1 | Startanforderung | 1 | Hex | 1 | Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client |
2 | Hardwaretyp | 1 | Hex | 1 | Art der Hardwareadresse in diesem Protokoll 1 - MAC |
3 | Hardware passt Länge | 6 | Hex | 1 | MAC-Adresslänge des Geräts |
4 | Hopfen | 1 | Hex | 1 | Anzahl der Zwischenstrecken |
5 | Transaktions-ID | 23: cf: de: 1d | Hex | 4 | Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert |
7 | Zweite verstrichen | 0 | Hex | 4 | Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse |
9 | Bootp-Flags | 0 | Hex | 2 | Einige Flags, die als Hinweis auf Protokollparameter 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 vorgeschlagene IP-Adresse (falls vorhanden) |
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. ein Switch) |
27 | Client-MAC-Adresse | 14: d6: 4d: a7: c9: 55 | Hex | 6 | MAC-Adresse des Absenders des Pakets (Client) |
31 | Auffüllen der Client-Hardware-Adresse | | Hex | 10 | Reservierter Platz. Normalerweise Nullen |
41 | Server-Hostname | | String | 64 | Der Name des DHCP-Servers. Normalerweise nicht übertragen |
105 | Name der Startdatei | | String | 128 | Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird |
235 | Magischer Keks | 63: 82: 53: 63 | Hex | 4 | Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört |
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen |
236 | Optionsnummer | 53 | Dez. | 1 | Option 53, die den Typ des DHCP-Pakets angibt
1 - DHCPDISCOVER 3 - DHCPREQUEST 2 - DHCPOFFER 5 - DHCPACK 8 - DHCPINFORM |
| Optionslänge | 1 | Dez. | 1 |
| Optionswert | 1 | Dez. | 1 |
| Optionsnummer | 50 | Dez. | 1 | Welche IP-Adresse möchte der Client erhalten? |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 172.16.134.61 | String | 4 |
| Optionsnummer | 55 | | 1 | Die 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änge | 8 | | 1 |
| Optionswert | 01: 03: 06: 0c: 0f: 1c: 42: 79 | | 8 |
| Optionsnummer | 82 | Dez. | | 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änge | 18 | Dez. | |
| Optionswert | 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 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-PaketstrukturtabellePaketposition | Name des Wertes (allgemein) | Beispiel | Einreichung | Byte | Erklärung |
1 | Startanforderung | 1 | Hex | 1 | Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client |
2 | Hardwaretyp | 1 | Hex | 1 | Art der Hardwareadresse in diesem Protokoll 1 - MAC |
3 | Hardware passt Länge | 6 | Hex | 1 | MAC-Adresslänge des Geräts |
4 | Hopfen | 1 | Hex | 1 | Anzahl der Zwischenstrecken |
5 | Transaktions-ID | 23: cf: de: 1d | Hex | 4 | Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert |
7 | Zweite verstrichen | 0 | Hex | 4 | Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse |
9 | Bootp-Flags | 0 | Hex | 2 | Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können. In diesem Fall bedeutet 0 den Unicast-Anforderungstyp |
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 vorgeschlagene IP-Adresse (falls vorhanden) |
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. ein Switch) |
27 | Client-MAC-Adresse | 14: d6: 4d: a7: c9: 55 | Hex | 6 | MAC-Adresse des Absenders des Pakets (Client) |
31 | Auffüllen der Client-Hardware-Adresse | | Hex | 10 | Reservierter Platz. Normalerweise Nullen |
41 | Server-Hostname | | String | 64 | Der Name des DHCP-Servers. Normalerweise nicht übertragen |
105 | Name der Startdatei | | String | 128 | Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird |
235 | Magischer Keks | 63: 82: 53: 63 | Hex | 4 | Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört |
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen |
236 | Optionsnummer | 53 | Dez. | 1 | Option 53, die den Typ des DHCP 2-Pakets angibt - DHCPOFFER |
| Optionslänge | 1 | Dez. | 1 |
| Optionswert | 2 | Dez. | 1 |
| Optionsnummer | 1 | Dez. | 1 | Option mit DHCP-Client-Netzwerkmaske |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 255.255.224.0 | String | 4 |
| Optionsnummer | 3 | Dez. | 1 | Option mit Standard-Gateway für DHCP-Clients |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 172.16.12.1 | String | 4 |
| Optionsnummer | 6 | Dez. | 1 | Option, die dem DNS-Client DHCP anbietet |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 8.8.8.8 | String | 4 |
| Optionsnummer | 51 | Dez. | 1 | Die Lebensdauer der ausgegebenen Netzwerkparameter in Sekunden. Danach muss der DHCP-Client sie erneut anfordern |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 86400 | Dez. | 4 |
| Optionsnummer | 82 | Dez. | 1 | Option 82 wiederholt, was in DHCPDISCOVER enthalten war |
| Optionslänge | 18 | Dez. | 1 |
| Optionswert | 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 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-PaketstrukturtabellePaketposition | Name des Wertes (allgemein) | Beispiel | Einreichung | Byte | Erklärung |
1 | Startanforderung | 1 | Hex | 1 | Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client |
2 | Hardwaretyp | 1 | Hex | 1 | Art der Hardwareadresse in diesem Protokoll 1 - MAC |
3 | Hardware passt Länge | 6 | Hex | 1 | MAC-Adresslänge des Geräts |
4 | Hopfen | 1 | Hex | 1 | Anzahl der Zwischenstrecken |
5 | Transaktions-ID | 23: cf: de: 1d | Hex | 4 | Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert |
7 | Zweite verstrichen | 0 | Hex | 4 | Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse |
9 | Bootp-Flags | 8000 | Hex | 2 | Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können. In diesem Fall "Broadcast" |
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 vorgeschlagene IP-Adresse (falls vorhanden) |
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. ein Switch) |
27 | Client-MAC-Adresse | 14: d6: 4d: a7: c9: 55 | Hex | 6 | MAC-Adresse des Absenders des Pakets (Client) |
31 | Auffüllen der Client-Hardware-Adresse | | Hex | 10 | Reservierter Platz. Normalerweise Nullen |
41 | Server-Hostname | | String | 64 | Der Name des DHCP-Servers. Normalerweise nicht übertragen |
105 | Name der Startdatei | | String | 128 | Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird |
235 | Magischer Keks | 63: 82: 53: 63 | Hex | 4 | Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört |
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen |
236 | Optionsnummer | 53 | Dez. | 3 | Option 53, die den Typ des DHCP 3-Pakets angibt - DHCPREQUEST |
| Optionslänge | 1 | Dez. | 1 |
| Optionswert | 3 | Dez. | 1 |
| Optionsnummer | 61 | Dez. | 1 | Client-ID: 01 (für Ehernet) + MAC-Adresse des Clients |
| Optionslänge | 7 | Dez. | 1 |
| Optionswert | 01: 2c: ab: 25: ff: 72: a6 | Hex | 7 |
| Optionsnummer | 60 | Dez. | | "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änge | 11 | Dez. | |
| Optionswert | udhcp 0.9.8 | String | |
| Optionsnummer | 55 | | 1 | Die 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änge | 8 | | 1 |
| Optionswert | 01: 03: 06: 0c: 0f: 1c: 42: 79 | | 8 |
| Optionsnummer | 82 | Dez. | 1 | Option 82 wiederholt, was in DHCPDISCOVER enthalten war |
| Optionslänge | 18 | Dez. | 1 |
| Optionswert | 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 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-PaketstrukturtabellePaketposition | Name des Wertes (allgemein) | Beispiel | Einreichung | Byte | Erklärung |
1 | Startanforderung | 2 | Hex | 1 | Art der Nachricht. 1 - Anfrage von Client zu Server, 2 - Antwort von Server zu Client |
2 | Hardwaretyp | 1 | Hex | 1 | Art der Hardwareadresse in diesem Protokoll 1 - MAC |
3 | Hardware passt Länge | 6 | Hex | 1 | MAC-Adresslänge des Geräts |
4 | Hopfen | 1 | Hex | 1 | Anzahl der Zwischenstrecken |
5 | Transaktions-ID | 23: cf: de: 1d | Hex | 4 | Eindeutige Transaktionskennung. Wird vom Client zu Beginn des Anforderungsvorgangs generiert |
7 | Zweite verstrichen | 0 | Hex | 4 | Zeit in Sekunden seit Beginn des Prozesses zum Abrufen der Adresse |
9 | Bootp-Flags | 8000 | Hex | 2 | Einige Flags, die als Hinweis auf Protokollparameter gesetzt werden können. In diesem Fall "Broadcast" |
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 vorgeschlagene IP-Adresse (falls vorhanden) |
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. ein Switch) |
27 | Client-MAC-Adresse | 14: d6: 4d: a7: c9: 55 | Hex | 6 | MAC-Adresse des Absenders des Pakets (Client) |
31 | Auffüllen der Client-Hardware-Adresse | | Hex | 10 | Reservierter Platz. Normalerweise Nullen |
41 | Server-Hostname | | String | 64 | Der Name des DHCP-Servers. Normalerweise nicht übertragen |
105 | Name der Startdatei | | String | 128 | Der Dateiname auf dem Server, der von plattenlosen Stationen während des Startvorgangs verwendet wird |
235 | Magischer Keks | 63: 82: 53: 63 | Hex | 4 | Die "magische" Zahl, mit der inkl. Sie können feststellen, dass dieses Paket zum DHCP-Protokoll gehört |
DHCP-Optionen. Kann in beliebiger Reihenfolge gehen |
236 | Optionsnummer | 53 | Dez. | 3 | Option 53 zur Angabe des DHCP-Pakettyps 5 - DHCPACK |
| Optionslänge | 1 | Dez. | 1 |
| Optionswert | 5 | Dez. | 1 |
| Optionsnummer | 1 | Dez. | 1 | Option mit DHCP-Client-Netzwerkmaske |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 255.255.224.0 | String | 4 |
| Optionsnummer | 3 | Dez. | 1 | Option mit Standard-Gateway für DHCP-Clients |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 172.16.12.1 | String | 4 |
| Optionsnummer | 6 | Dez. | 1 | Option, die dem DNS-Client DHCP anbietet |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 8.8.8.8 | String | 4 |
| Optionsnummer | 51 | Dez. | 1 | Die Lebensdauer der ausgegebenen Netzwerkparameter in Sekunden. Danach muss der DHCP-Client sie erneut anfordern |
| Optionslänge | 4 | Dez. | 1 |
| Optionswert | 86400 | Dez. | 4 |
| Optionsnummer | 82 | Dez. | 1 | Option 82 wiederholt, was in DHCPDISCOVER enthalten war |
| Optionslänge | 18 | Dez. | 1 |
| Optionswert | 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 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 BeispieldatenbankstrukturAbfrageabschnitt: 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;)