DHCP + Mysql server op Python

DHCP + Mysql server op Python

Het doel van dit project was:

  • Het bestuderen van het DHCP-protocol bij werking in een IPv4-netwerk
  • Het leren van Python (een beetje meer dan vanaf nul 😉 )
  • vervanging van de server DB2DHCP (mijn fork), het origineel hier, dat steeds moeilijker te bouwen is voor het nieuwe besturingssysteem. En ik vind het niet leuk dat de binaire versie niet 'direct kan worden veranderd'
  • het verkrijgen van een werkende DHCP-server met de mogelijkheid om het IP-adres van de abonnee op te halen op basis van het mac-adres van de abonnee of de combinatie van mac-switch + poort (Optie 82)
  • het schrijven van weer een fiets (Oh! dat is mijn favoriete bezigheid)
  • de klappen ontvangen over mijn onhandigheid op HabrHabr (of liever een uitnodiging) 😉

Resultaat: het werkt 😉 Uitgetest op FreeBSD en Ubuntu. Theoretisch kan de code ook onder elk besturingssysteem werken, omdat er geen specifieke afhankelijkheden in de code lijken te zijn.
Voorzichtig! Er komt veel meer.

Link naar de repository voor liefhebbers om 'het in het echt te voelen'.

Het proces van installatie, configuratie en gebruik van het resultaat van het 'studeren van de materie' staat verderop, en hierna een beetje theorie over het DHCP-protocol. Voor mezelf. En voor de geschiedenis 😉

Een beetje theorie

Wat is DHCP

Dit is een netwerkprotocol dat een apparaat in staat stelt zijn IP-adres te kennen (en andere parameters zoals de gateway, DNS, enz.), van de DHCP-server. De pakketwisseling gebeurt via het UDP-protocol. Het algemene principe van het werken van een apparaat bij het opvragen van netwerkparameters is als volgt:

  1. Een apparaat (klant) verstuurt een broadcast UDP-verzoek (DHCPDISCOVER) door het hele netwerk met de vraag 'heeft iemand een IP-adres voor mij?'. Meestal (maar niet altijd) vindt het verzoek plaats vanaf poort 68 (bron), en de bestemming is poort 67 (doel). Sommige apparaten sturen ook pakketten vanaf poort 67. In het DHCPDISCOVER-pakket is het MAC-adres van het klantapparaat opgenomen.
  2. Alle DHCP-servers die zich in het netwerk bevinden (en er kunnen er meerdere zijn), vormen een DHCPOFFER-aanbieding met netwerkinstellingen voor het apparaat dat DHCPDISCOVER heeft verzonden, en sturen deze eveneens broadcast door het netwerk. De identificatie van wie dit pakket bedoeld is, gebeurt aan de hand van het MAC-adres van de klant dat eerder in het DHCPDISCOVER-verzoek is verstrekt.
  3. De cliënt ontvangt pakketten met netwerkinstellingen, kiest de meest aantrekkelijke (de criteria kunnen verschillend zijn, bijvoorbeeld ook op basis van de levertijd van het pakket, het aantal tussenliggende routes), en doet met de gewenste DHCP-server een 'officiële aanvraag' DHCPREQUEST met netwerkinstellingen. In dit geval gaat het pakket al naar een specifieke DHCP-server.
  4. De server die de DHCPREQUEST ontvangt, stuurt een pakket in het formaat DHCPACK, waarin opnieuw de netwerkinstellingen die voor deze cliënt bestemd zijn, worden vermeld.

DHCP + Mysql server op Python

Bovendien zijn er DHCPINFORM-pakketten die vanuit de cliënt verzonden worden en die als doel hebben de DHCP-server te informeren dat de 'cliënt leeft' en gebruik maakt van de verstrekte netwerkinstellingen. In de implementatie van deze server worden deze pakketten genegeerd.

Pakketformaten

Over het algemeen ziet het Ethernet-pakket eruit als volgt:

DHCP + Mysql server op Python

In ons geval zullen we alleen de gegevens van de inhoud van het UDP-pakket beschouwen, zonder de headers van de OSI-protocolniveaus, met name de structuur van DHCP:

DHCPDISCOVER

Dus het proces van het verkrijgen van een IP-adres voor een apparaat begint met het feit dat de DHCP-cliënt een broadcastaanroep met poort 68 naar 255.255.255.255:67 verzendt. In dit pakket omvat de cliënt zijn MAC-adres en wat hij precies van de DHCP-server wil ontvangen. De structuur van het pakket wordt hieronder in tabelvorm beschreven.

Tabel met de structuur van het DHCPDISCOVER-pakket

Positie in het pakket
Naam van de waarde
Voorbeeld
Representatie
Bytes
Uitleg

1
Boot Request
1
Hex
1
Type bericht. 1 — verzoek van cliënt naar server, 2 — antwoord van server naar cliënt.

2
Hardware type
1
Hex
1
Type hardware adres, in dit protocol 1 — MAC.

3
Hardware adreslengte
6
Hex
1
Lengte van het MAC-adres van het apparaat.

4
Hops
1
Hex
1
Aantal tussenliggende routes.

5
Transactie-ID
23:cf:de:1d
Hex
4
Unieke identificatie voor de transactie. Wordt door de cliënt gegenereerd aan het begin van de aanvraagoperatie.

7
Vervlogen tijd
0
Hex
4
Tijd in seconden vanaf het begin van het proces van het verkrijgen van een adres.

9
Bootp-vlaggen
0
Hex
2
Enkele vlaggen die ingesteld kunnen worden als aanwijzing voor protocolparameters.

11
Client IP-adres
0.0.0.0
String
4
IP-adres van de cliënt (indien aanwezig).

15
Jouw client IP-adres
0.0.0.0
String
4
IP-adres dat door de server is voorgesteld (indien aanwezig).

19
Volgende server IP-adres
0.0.0.0
String
4
IP-adres van de server (indien bekend).

23
Relay agent IP-adres
172.16.114.41
String
4
IP-adres van de relay-agent (bijv. switch).

27
Client MAC-adres
14:d6:4d:a7:c9:55
Hex
6
MAC-adres van de verzender van het pakket (de cliënt).

31
Padding voor client hardware-adres
 
Hex
10
Gereserveerde ruimte. Gewoonlijk gevuld met nullen.

41
Server hostnaam
 
String
64
Naam van de DHCP-server. Gewoonlijk niet verstuurd.

105
Boot bestandsnaam
 
String
128
Bestandsnaam op de server, gebruikt door schijfloze stations bij het opstarten

235
Magic cookie
63:82:53:63
Hex
4
Het 'magische' nummer waarmee onder andere kan worden bepaald dat dit pakket behoort tot het DHCP-protocol

DHCP-opties. Kunnen in willekeurige volgorde komen

236
Optienummer
53
Dec
1
Optie 53, die het type DHCP-pakket definieert

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

 
Optielengte
1
Dec
1

 
Optiewaarde
1
Dec
1

 
Optienummer
50
Dec
1
Welk IP-adres de client wil ontvangen

 
Optielengte
4
Dec
1

 
Optiewaarde
172.16.134.61
String
4

 
Optienummer
55
 
1
Netwerkparameters die door de client worden aangevraagd. De samenstelling kan variëren

01 — Subnetmasker
03 — Gateway
06 — DNS
oc — Hostnaam
0f — Netwerk domeinnaam
1c — Broadcast-adres
42 — TFTP-servernaam
79 — Classless Static Route

 
Optielengte
8
 
1

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

 
Optienummer
82
Dec
 
Optie 82, waarin het MAC-adres van het apparaat — de repeater en enige aanvullende waarden worden doorgegeven.

Meestal — de poort van de switch waar de eindclient van DHCP op werkt. In deze optie zijn 'ingebedde' aanvullende parameters. Het eerste byte is het nummer van de 'suboptie', het tweede de lengte, en daarna volgt de waarde.

In dit geval zijn in optie 82 de subopties ingebed:
Agent Circuit ID = 00:04:00:01:00:04, waarbij de laatste twee bytes de poort van de DHCP-client zijn waar het verzoek vandaan kwam

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC-adres van het apparaat van de DHCP-repeater

 
Optielengte
18
Dec
 

 
Optiewaarde
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Einde van het pakket
255
Dec
1
255 symboliseert het einde van het pakket

DHCPOFFER

Zodra de server het DHCPDISCOVER-pakket ontvangt en ziet dat hij iets kan aanbieden aan de client dat is aangevraagd, stelt hij een antwoord samen — DHCPDISCOVER. Het antwoord wordt verzonden naar de poort 'van waar het kwam', in een broadcast, omdat de client op dat moment nog geen IP-adres heeft, wat betekent dat hij het pakket alleen kan ontvangen als het breed verstuurd is. De client herkent dat dit pakket voor hem is aan zijn MAC-adres binnen het pakket, evenals het transactie-ID, dat hij genereert op het moment dat hij het eerste pakket aanmaakt.

Tabel van de pakketstructuur DHCPOFFER

Positie in het pakket
Naam van de waarde (gangbare naam)
Voorbeeld
Representatie
Bytes
Uitleg

1
Boot Request
1
Hex
1
Type bericht. 1 — verzoek van cliënt naar server, 2 — antwoord van server naar cliënt.

2
Hardware type
1
Hex
1
Type hardware adres, in dit protocol 1 — MAC.

3
Hardware adreslengte
6
Hex
1
Lengte van het MAC-adres van het apparaat.

4
Hops
1
Hex
1
Aantal tussenliggende routes.

5
Transactie-ID
23:cf:de:1d
Hex
4
Unieke identificatie voor de transactie. Wordt door de cliënt gegenereerd aan het begin van de aanvraagoperatie.

7
Vervlogen tijd
0
Hex
4
Tijd in seconden vanaf het begin van het proces van het verkrijgen van een adres.

9
Bootp-vlaggen
0
Hex
2
Bepaalde vlaggen die kunnen worden ingesteld als indicatie van protocolparameters. In dit geval betekent 0 — de type aanvraag Unicast

11
Client IP-adres
0.0.0.0
String
4
IP-adres van de cliënt (indien aanwezig).

15
Jouw client IP-adres
172.16.134.61
String
4
IP-adres dat door de server is voorgesteld (indien aanwezig).

19
Volgende server IP-adres
0.0.0.0
String
4
IP-adres van de server (indien bekend).

23
Relay agent IP-adres
172.16.114.41
String
4
IP-adres van de relay-agent (bijv. switch).

27
Client MAC-adres
14:d6:4d:a7:c9:55
Hex
6
MAC-adres van de verzender van het pakket (de cliënt).

31
Padding voor client hardware-adres
 
Hex
10
Gereserveerde ruimte. Gewoonlijk gevuld met nullen.

41
Server hostnaam
 
String
64
Naam van de DHCP-server. Gewoonlijk niet verstuurd.

105
Boot bestandsnaam
 
String
128
Bestandsnaam op de server, gebruikt door schijfloze stations bij het opstarten

235
Magic cookie
63:82:53:63
Hex
4
Het 'magische' nummer waarmee onder andere kan worden bepaald dat dit pakket behoort tot het DHCP-protocol

DHCP-opties. Kunnen in willekeurige volgorde komen

236
Optienummer
53
Dec
1
Optie 53, die het type DHCP-pakket definieert 2 — DHCPOFFER

 
Optielengte
1
Dec
1

 
Optiewaarde
2
Dec
1

 
Optienummer
1
Dec
1
Optie die de DHCP-client een subnetmasker aanbiedt

 
Optielengte
4
Dec
1

 
Optiewaarde
255.255.224.0
String
4

 
Optienummer
3
Dec
1
Optie die de DHCP-client een standaardgateway aanbiedt

 
Optielengte
4
Dec
1

 
Optiewaarde
172.16.12.1
String
4

 
Optienummer
6
Dec
1
Optie die de DHCP-client een DNS-aanbieding geeft

 
Optielengte
4
Dec
1

 
Optiewaarde
8.8.8.8
String
4

 
Optienummer
51
Dec
1
Tijdspanne in seconden voor de eerder verstrekte netwerkparameters, waarna de DHCP-client ze opnieuw moet aanvragen

 
Optielengte
4
Dec
1

 
Optiewaarde
86400
Dec
4

 
Optienummer
82
Dec
1
Optie 82, herhaalt wat in DHCPDISCOVER is aangekomen

 
Optielengte
18
Dec
1

 
Optiewaarde
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Einde van het pakket
255
Dec
1
255 symboliseert het einde van het pakket

DHCPREQUEST

Nadat de klant een DHCPOFFER heeft ontvangen, vormt hij een pakket met een verzoek om netwerkinstellingen, niet meer naar alle DHCP-servers in het netwerk, maar alleen naar één specifieke, wiens DHCPOFFER-voorstel hem het meest "aansprakelijk" lijkt. De criteria voor "aansprakelijk" kunnen verschillend zijn en zijn afhankelijk van de implementatie van de DHCP-client. De ontvanger van het verzoek wordt aangegeven met het MAC-adres van de DHCP-server. Het DHCPREQUEST-pakket kan ook door de client worden verzonden zonder eerder een DHCPDISCOVER te hebben gevormd, als het IP-adres van de server al eerder was verkregen.

Tabel van de structuur van het DHCPREQUEST-pakket

Positie in het pakket
Naam van de waarde (gangbare naam)
Voorbeeld
Representatie
Bytes
Uitleg

1
Boot Request
1
Hex
1
Type bericht. 1 — verzoek van cliënt naar server, 2 — antwoord van server naar cliënt.

2
Hardware type
1
Hex
1
Type hardware adres, in dit protocol 1 — MAC.

3
Hardware adreslengte
6
Hex
1
Lengte van het MAC-adres van het apparaat.

4
Hops
1
Hex
1
Aantal tussenliggende routes.

5
Transactie-ID
23:cf:de:1d
Hex
4
Unieke identificatie voor de transactie. Wordt door de cliënt gegenereerd aan het begin van de aanvraagoperatie.

7
Vervlogen tijd
0
Hex
4
Tijd in seconden vanaf het begin van het proces van het verkrijgen van een adres.

9
Bootp-vlaggen
8000
Hex
2
Bepaalde vlaggen die kunnen worden ingesteld als aanwijzing voor de protocolparameters. In dit geval is de 'broadcast' ingesteld.

11
Client IP-adres
0.0.0.0
String
4
IP-adres van de cliënt (indien aanwezig).

15
Jouw client IP-adres
172.16.134.61
String
4
IP-adres dat door de server is voorgesteld (indien aanwezig).

19
Volgende server IP-adres
0.0.0.0
String
4
IP-adres van de server (indien bekend).

23
Relay agent IP-adres
172.16.114.41
String
4
IP-adres van de relay-agent (bijv. switch).

27
Client MAC-adres
14:d6:4d:a7:c9:55
Hex
6
MAC-adres van de verzender van het pakket (de cliënt).

31
Padding voor client hardware-adres
 
Hex
10
Gereserveerde ruimte. Gewoonlijk gevuld met nullen.

41
Server hostnaam
 
String
64
Naam van de DHCP-server. Gewoonlijk niet verstuurd.

105
Boot bestandsnaam
 
String
128
Bestandsnaam op de server, gebruikt door schijfloze stations bij het opstarten

235
Magic cookie
63:82:53:63
Hex
4
Het 'magische' nummer waarmee onder andere kan worden bepaald dat dit pakket behoort tot het DHCP-protocol

DHCP-opties. Kunnen in willekeurige volgorde komen

236
Optienummer
53
Dec
3
Optie 53, die het type DHCP-pakket definieert 3 — DHCPREQUEST

 
Optielengte
1
Dec
1

 
Optiewaarde
3
Dec
1

 
Optienummer
61
Dec
1
Klantidentificator: 01 (voor Ethernet) + MAC-adres van de klant

 
Optielengte
7
Dec
1

 
Optiewaarde
01:2c:ab:25:ff:72:a6
Hex
7

 
Optienummer
60
Dec
 
"Vendor class identifier". In mijn geval geeft het de versie van de DHCP-client aan. Andere apparaten kunnen iets anders teruggeven. Windows geeft bijvoorbeeld MSFT 5.0 aan.

 
Optielengte
11
Dec
 

 
Optiewaarde
udhcp 0.9.8
String
 

 
Optienummer
55
 
1
Netwerkparameters die door de client worden aangevraagd. De samenstelling kan variëren

01 — Subnetmasker
03 — Gateway
06 — DNS
oc — Hostnaam
0f — Netwerk domeinnaam
1c — Broadcast-adres
42 — TFTP-servernaam
79 — Classless Static Route

 
Optielengte
8
 
1

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

 
Optienummer
82
Dec
1
Optie 82, herhaalt wat in DHCPDISCOVER is aangekomen

 
Optielengte
18
Dec
1

 
Optiewaarde
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Einde van het pakket
255
Dec
1
255 symboliseert het einde van het pakket

DHCPACK

Als bevestiging dat "ja, dit is jouw IP-adres, en ik zal het aan niemand anders geven" van de DHCP-server, dient het pakket in het DHCPACK-formaat van de server aan de client. Het wordt net als de andere pakketten breed uitgezonden. Echter, in de onderstaande code van de DHCP-server, geïmplementeerd in Python, dupliceer ik voor de zekerheid elk breed uitzendverzoek door een pakket naar een specifieke IP van de client te verzenden, als deze al bekend is. Bovendien maakt de DHCP-server zich helemaal geen zorgen dat het DHCPACK-pakket de client heeft bereikt. Als de client geen DHCPACK ontvangt, herhaalt hij simpelweg het DHCPREQUEST na een bepaalde tijd.

Tabel van de structuur van het DHCPACK-pakket

Positie in het pakket
Naam van de waarde (gangbare naam)
Voorbeeld
Representatie
Bytes
Uitleg

1
Boot Request
2
Hex
1
Type bericht. 1 — verzoek van cliënt naar server, 2 — antwoord van server naar cliënt.

2
Hardware type
1
Hex
1
Type hardware adres, in dit protocol 1 — MAC.

3
Hardware adreslengte
6
Hex
1
Lengte van het MAC-adres van het apparaat.

4
Hops
1
Hex
1
Aantal tussenliggende routes.

5
Transactie-ID
23:cf:de:1d
Hex
4
Unieke identificatie voor de transactie. Wordt door de cliënt gegenereerd aan het begin van de aanvraagoperatie.

7
Vervlogen tijd
0
Hex
4
Tijd in seconden vanaf het begin van het proces van het verkrijgen van een adres.

9
Bootp-vlaggen
8000
Hex
2
Bepaalde vlaggen die kunnen worden ingesteld als aanwijzing voor de protocolparameters. In dit geval is de 'broadcast' ingesteld.

11
Client IP-adres
0.0.0.0
String
4
IP-adres van de cliënt (indien aanwezig).

15
Jouw client IP-adres
172.16.134.61
String
4
IP-adres dat door de server is voorgesteld (indien aanwezig).

19
Volgende server IP-adres
0.0.0.0
String
4
IP-adres van de server (indien bekend).

23
Relay agent IP-adres
172.16.114.41
String
4
IP-adres van de relay-agent (bijv. switch).

27
Client MAC-adres
14:d6:4d:a7:c9:55
Hex
6
MAC-adres van de verzender van het pakket (de cliënt).

31
Padding voor client hardware-adres
 
Hex
10
Gereserveerde ruimte. Gewoonlijk gevuld met nullen.

41
Server hostnaam
 
String
64
Naam van de DHCP-server. Gewoonlijk niet verstuurd.

105
Boot bestandsnaam
 
String
128
Bestandsnaam op de server, gebruikt door schijfloze stations bij het opstarten

235
Magic cookie
63:82:53:63
Hex
4
Het 'magische' nummer waarmee onder andere kan worden bepaald dat dit pakket behoort tot het DHCP-protocol

DHCP-opties. Kunnen in willekeurige volgorde komen

236
Optienummer
53
Dec
3
Optie 53, die het type DHCP-pakket definieert 5 — DHCPACK

 
Optielengte
1
Dec
1

 
Optiewaarde
5
Dec
1

 
Optienummer
1
Dec
1
Optie die de DHCP-client een subnetmasker aanbiedt

 
Optielengte
4
Dec
1

 
Optiewaarde
255.255.224.0
String
4

 
Optienummer
3
Dec
1
Optie die de DHCP-client een standaardgateway aanbiedt

 
Optielengte
4
Dec
1

 
Optiewaarde
172.16.12.1
String
4

 
Optienummer
6
Dec
1
Optie die de DHCP-client een DNS-aanbieding geeft

 
Optielengte
4
Dec
1

 
Optiewaarde
8.8.8.8
String
4

 
Optienummer
51
Dec
1
Tijdspanne in seconden voor de eerder verstrekte netwerkparameters, waarna de DHCP-client ze opnieuw moet aanvragen

 
Optielengte
4
Dec
1

 
Optiewaarde
86400
Dec
4

 
Optienummer
82
Dec
1
Optie 82, herhaalt wat in DHCPDISCOVER is aangekomen

 
Optielengte
18
Dec
1

 
Optiewaarde
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Einde van het pakket
255
Dec
1
255 symboliseert het einde van het pakket

Installatie

De installatie bestaat in feite uit het installeren van de benodigde Python-modules voor het functioneren. Er wordt aangenomen dat MySQL al is geïnstalleerd en geconfigureerd.

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

We creëren een MySQL-database, plaatsen er een dump van pydhcp.sql in en configureren het configuratiebestand.

Configuratie

Alle serverinstellingen bevinden zich in een xml-geformatteerd bestand. Referentiebestand:

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

Laten we de tags eens in detail bekijken:

De sectie dhcpserver beschrijft de belangrijke instellingen voor het opstarten van de server, namelijk:

  • host — welk IP-adres de server luistert op poort 67
  • broadcast — welk IP het broadcastadres is voor DHCPOFFER en DHCPACK
  • DHCPServer — welk IP het DHCP-server heeft
  • LeaseTime is de huurtijd van het uitgegeven IP-adres
  • ThreadLimit — hoeveel gelijktijdige threads zijn er voor de verwerking van binnenkomende UDP-pakketten op poort 67. Het wordt verondersteld dat dit zal helpen bij hoogbelaste projecten 😉
  • defaultMask, defaultRouter, defaultDNS — de standaardinstellingen die aan de abonnee worden aangeboden, als het IP in de database wordt gevonden, maar aanvullende parameters voor hem niet zijn opgegeven

De sectie mysql:

host, username, password, basename — dat spreekt voor zich. De verwachte structuur van de database is beschikbaar op GitHub

De sectie query: hier worden de verzoeken beschreven voor het verkrijgen van OFFER/ACK:

  • offer_count — het aantal regels in de verzoeken die een resultaat van het type ip, mask, router, dns teruggeven
  • offer_n — de regel van het verzoek. Als de terugkeer leeg is, wordt de volgende aanvraag offer uitgevoerd
  • history_sql — de aanvraag die bijvoorbeeld in de "authenticatiegeschiedenis" schrijft voor de abonnee

In de verzoeken kunnen alle variabelen uit de sectie options of opties uit het DHCP-protocol worden gebruikt.

De sectie options. Hier wordt het interessanter. Hier kunnen we variabelen maken die we later kunnen gebruiken in de sectie query.

Bijvoorbeeld:

option_82_hex:sw_port1:20:22

, dit commando pakt de volledige regel die is ontvangen in het DHCP-verzoek optie 82, in hex-formaat, bereik van 20 tot en met 22 byte en plaatst deze in een nieuwe variabele sw_port1 (switchpoort van waar het verzoek vandaan kwam)

option_82_hex:sw_mac:26:40

, we definiëren de variabele sw_mac door hex te nemen uit het bereik 26:40

Om alle mogelijke opties te zien die in verzoeken kunnen worden gebruikt, kan de server worden gestart met de sleutel -d. We zien ongeveer zo'n logboek:

--pakket DHCPINFORM ontvangen op poort 67, van 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,
 'ReqListPerfowmRouterDiscover': 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'}

Bijgevolg kunnen we elke variabele omhullen in {} en zal deze worden gebruikt in de SQL-query.

Laten we voor de geschiedenis vastleggen welk IP-adres de klant heeft ontvangen:

DHCP + Mysql server op Python

DHCP + Mysql server op Python

Server starten

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

— d modus van uitvoer in de DEBUG-console
— c <bestandsnaam> configuratiebestand

Vluchtanalyses

Laten we nu dieper ingaan op de implementatie van de server in Python. Dit is zwaar. Python is ‘on-the-fly’ geleerd. Veel dingen zijn gedaan in de stijl van: ‘oeh, zo heb ik het toch werkend gekregen’. Helemaal niet geoptimaliseerd en in deze staat gelaten vanwege het gebrek aan ervaring in ontwikkeling met python. Ik ga me richten op de meest interessante punten van de serverimplementatie in ‘code’.

Parser voor het configuratiebestand XML

Er wordt gebruik gemaakt van de standaard Python-module xml.dom. Het lijkt eenvoudig, maar bij de implementatie ontbrak het aan bruikbare documentatie en voorbeelden op internet voor dit specifieke module.

    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

Het is opmerkelijk dat multithreading in Python heel begrijpelijk en eenvoudig is geïmplementeerd.

def PacketWork(data,addr): 
...
# implementatie van het ontleden van het ontvangen pakket en het antwoord daarop
...


while True:
    data, addr = udp_socket.recvfrom(1024) # wachten op UDP-pakket
    thread = threading.Thread(target=PacketWork, args=(data,addr,)).start()	# zodra het binnenkomt - starten we op de achtergrond de eerder gedefinieerde functie PacketWork met parameters
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # als het aantal actieve threads groter is dan in de instellingen, wachten we totdat het minder wordt

Ontvangen/verzenden van DHCP-pakket

Om UDP-pakketten die via de netwerkkaart binnenkomen te onderscheppen, moet je de socket 'oprichten':

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

, waar de vlaggen zijn:

  • AF_INET betekent dat het adresformaat IP: poort zal zijn. Er kan ook AF_UNIX zijn — waar het adres wordt opgegeven met een bestandsnaam.
  • SOCK_DGRAM betekent dat we geen "ruwe pakket" ontvangen, maar een pakket dat al door de firewall is gegaan, en waarbij het pakket gedeeltelijk is afgebroken. Dus we krijgen alleen het UDP-pakket zonder de "fysieke" component van de UDP-pakketwrapper. Als we de vlag SOCK_RAW gebruiken, moeten we ook deze "wrapper" parseren.

Het verzenden van een pakket kan zowel via een broadcast zijn:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # schakelen de socket in de broadcast-verzendmodus
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, alsook naar het adres waar het pakket vandaan kwam:

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # schakelen de socket in de "meerdere luisteraars" modus
                        rz=udp_socket.sendto(packetack, addr)

, waarbij SOL_SOCKET het "protocolniveau" aangeeft voor het instellen van opties,

, SO_BROADCAST is de optie die aangeeft dat het pakket via "broadcast" wordt verzonden,

  , SO_REUSEADDR is de optie die de socket in de "meerdere luisteraars" modus schakelt. In principe is deze niet nodig in dit geval, maar op een van de FreeBSD-servers waarop ik testte, werkte de code zonder deze optie niet.

Het ontleden van het DHCP-pakket

Hier vond ik Python echt geweldig. Blijkt dat het vanuit de "doos" vrij gemakkelijk omgaat met bytecode. Het maakt het heel eenvoudig om deze om te zetten naar decimale waarden, strings en hex – dat is precies wat we nodig hebben om de structuur van het pakket te begrijpen. Zo kun je bijvoorbeeld een reeks bytes in HEX en gewone bytes krijgen:

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

, de bytes verpakken in een structuur:

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

Een IP uit de structuur verkrijgen:

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

En omgekeerd:

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

Dat is alles 😉

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster