DHCP+Mysql server på Python

DHCP+Mysql server på Python

Målet med dette projekt var:

  • Undersøgelse af DHCP-protokollen ved arbejde i IPv4-netværk
  • Læring af Python (lidt mere end fra bunden 😉)
  • erstatning af server DB2DHCP (min fork), originalen her, som det bliver sværere og sværere at compilere til det nye OS. Og jeg bryder mig ikke om, at det binære, som man ikke kan "skifte lige nu"
  • opnåelse af en fungerende DHCP-server med mulighed for at udtrække IP-adressen for abonnenten baseret på abonnentens MAC-adresse eller kombinationen af MAC-adresse plus switch+port (Option 82)
  • skrivning af endnu en cykel (Åh! Det er min yndlingsbeskæftigelse)
  • få kritik for mine klodsede handlinger på Habrhabr (eller bedre, en invitation) 😉

Resultat: fungerer 😉 Afprøvet på OS FreeBSD og Ubuntu. Teoretisk kan koden tilpasses til at fungere på ethvert OS, da der ikke tilsyneladende er specifikke bind i koden.
Pas på! Der er meget mere.

Link til repository for dem, der vil "røre ved det i virkeligheden".

Processen med at installere, konfigurere og bruge resultatet af "studiet af materien" er meget længere nede, og lidt teori om DHCP-protokollen følger. Til mig selv. Og til historien 😉

Lidt teori

Hvad er DHCP

Det er en netværksprotokol, der gør det muligt for en enhed at kende sin IP-adresse (og andre parametre som gateway, DNS osv.) fra DHCP-serveren. Pakkeudvekslingen sker via UDP-protokollen. Den generelle arbejdsprincip for enheden ved anmodning om netværksparametre er følgende:

  1. Enhed (klient) sender en broadcast UDP-anmodning (DHCPDISCOVER) over hele netværket med en anmodning om "nå, nogen, giv mig en IP-adresse". Normalt (men ikke altid) sker anmodningen fra port 68 (source), mens destinationsporten er 67 (destination). Nogle enheder sender også pakker fra port 67. Inden i DHCPDISCOVER-pakken er MAC-adressen for klienten inkluderet.
  2. Alle DHCP-servere, der er til stede i netværket (og der kan være flere), danner et tilbud DHCPOFFER med netværksindstillinger til den enhed, der sendte DHCPDISCOVER, og sender det også broadcast over netværket. Identifikationen af, hvem denne pakke er til, sker via MAC-adressen for klienten, som blev givet tidligere i DHCPDISCOVER-anmodningen.
  3. Klienten accepterer pakker med netværksindstillinger, vælger den mest attraktive (kriterierne kan variere, f.eks. også efter leveringstid for pakken, antal mellemruter) og sender en "officiel anmodning" DHCPREQUEST med netværksindstillingerne til den ønskede DHCP-server. I dette tilfælde går pakken direkte til den specifikke DHCP-server.
  4. Serveren, der modtog DHCPREQUEST, sender en pakke i formatet DHCPACK, som endnu en gang opremser netværksindstillingerne, der er beregnet til denne klient.

DHCP+Mysql server på Python

Desuden er der DHCPINFORM-pakker, der sendes fra klienten, hvis formål er at informere DHCP-serveren om, at "klienten er aktiv" og bruger de tildelte netværksindstillinger. Disse pakker ignoreres i denne servers implementering.

Pakkeformat

Generelt ser Ethernet-pakkens ramme således ud:

DHCP+Mysql server på Python

I vores tilfælde vil vi kun se på dataene i indholdet af UDP-pakken, uden protokollens header på OSI-niveauerne, nemlig DHCP-strukturen:

DHCPDISCOVER

Processen til at modtage en IP-adresse for en enhed starter med, at DHCP-klienten sender en broadcast-anmodning fra port 68 til 255.255.255.255:67. I denne pakke inkluderer klienten sin MAC-adresse og hvad han ønsker at modtage fra DHCP-serveren. Pakkeformatet er beskrevet i tabellen nedenfor.

Tabel over strukturen for DHCPDISCOVER-pakken

Position i pakken
Værdiens navn
Eksempel
booking
Byte
Forklaring

1
Boot Request
1
Hex
1
Meddelelsestype. 1 — anmodning fra klient til server, 2 — svar fra server til klient

2
Hardware type
1
Hex
1
Type af hardwareadresse; i dette protokol 1 — MAC

3
Hardware adresses længde
6
Hex
1
Længden af enhedens MAC-adresse

4
Hops
1
Hex
1
Antal mellemruter

5
Transaktions-id
23:cf:de:1d
Hex
4
Unik identifikator for transaktionen. Genereres af klienten i begyndelsen af anmodningsoperationen

7
Sekunder forløbet
0
Hex
4
Tiden i sekunder siden starten af processen til at få adressen

9
Bootp flag
0
Hex
2
Nogle flag, der kan oprettes for at angive protokolparametre

11
Klientens IP-adresse
0.0.0.0
Streng
4
Klientens IP-adresse (hvis der er nogen)

15
Din klient IP-adresse
0.0.0.0
Streng
4
IP-adresse foreslået af serveren (hvis der er nogen)

19
Næste servers IP-adresse
0.0.0.0
Streng
4
Serverens IP-adresse (hvis kendt)

23
Relay agent IP-adresse
172.16.114.41
Streng
4
IP-adressen på relay-agent (f.eks. switch)

27
Klientens MAC-adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-adressen på afsenderen af pakken (klienten)

31
Klientens hardwareadresse padding
 
Hex
10
Reserveret plads. Normalt fyldt med nuller

41
Serverens vært navn
 
Streng
64
Navn på DHCP-serveren. Normalt ikke sendt

105
Boot-filnavn
 
Streng
128
Filnavn på serveren, der bruges af diskfrie stationer under opstart

235
Magic cookie
63:82:53:63
Hex
4
«Magisk» nummer, som blandt andet bruges til at identificere, at denne pakke tilhører DHCP-protokollen

DHCP-muligheder. De kan komme i vilkårlig rækkefølge

236
Mulighedens nummer
53
Dec
1
Mulighed 53, der definerer typen af DHCP-pakke

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

 
Mulighedens længde
1
Dec
1

 
Mulighedens værdi
1
Dec
1

 
Mulighedens nummer
50
Dec
1
Hvilken IP-adresse klienten ønsker at opnå

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
172.16.134.61
Streng
4

 
Mulighedens nummer
55
 
1
Netværksparametre, som klienten anmoder om. Indholdet kan variere

01 — Netværksmaske
03 — Gateway
06 — DNS
oc — Værtsnavn
0f — netværksdomænenavn
1c — adresse til broadcast-anmodning
42 — TFTP-serverens navn
79 — Classless Static Route

 
Mulighedens længde
8
 
1

 
Mulighedens værdi
01:03:06:0c:0f:1c:42:79
 
8

 
Mulighedens nummer
82
Dec
 
Mulighed 82, der videregiver MAC-adressen på enheden — retransmitteren og nogle ekstra værdier.

Ofte — porten på switchen, hvor den afsluttende DHCP-klient arbejder. I denne mulighed er der "indlejrede" ekstra parametre. Den første byte er nummeret på "undermuligheden", den anden dens længde, derefter dens værdi.

I dette tilfælde indeholder mulighed 82 underindstillingerne:
Agent Circuit ID = 00:04:00:01:00:04, hvor de to sidste bytes er porten på DHCP-klienten, hvorfra anmodningen kom

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC-adressen på retransmitteren for DHCP

 
Mulighedens længde
18
Dec
 

 
Mulighedens værdi
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Pakkeafslutning
255
Dec
1
255 symboliserer afslutningen på pakken

DHCPOFFER

Så snart serveren modtager DHCPDISCOVER-pakken og ser, at den kan tilbyde noget til klienten baseret på det anmodede, danner den et svar — DHCPDISCOVER. Svaret sendes til den port, som anmodningen kom fra, som broadcast, da klienten på dette tidspunkt endnu ikke har en IP-adresse, og derfor kan den kun modtage pakken, hvis den sendes som broadcast. Klienten genkender, at dette er pakken til den via sin MAC-adresse indeni pakken samt transaktionens nummer, som den genererer, når den opretter den første pakke.

Tabel over DHCPOFFER-pakkestruktur

Position i pakken
Navnet på værdien (almindelig brugt)
Eksempel
booking
Byte
Forklaring

1
Boot Request
1
Hex
1
Meddelelsestype. 1 — anmodning fra klient til server, 2 — svar fra server til klient

2
Hardware type
1
Hex
1
Type af hardwareadresse; i dette protokol 1 — MAC

3
Hardware adresses længde
6
Hex
1
Længden af enhedens MAC-adresse

4
Hops
1
Hex
1
Antal mellemruter

5
Transaktions-id
23:cf:de:1d
Hex
4
Unik identifikator for transaktionen. Genereres af klienten i begyndelsen af anmodningsoperationen

7
Sekunder forløbet
0
Hex
4
Tiden i sekunder siden starten af processen til at få adressen

9
Bootp flag
0
Hex
2
Nogle flag, der kan indstilles som indikatorer for protokolparametre. I dette tilfælde betyder 0 — unicast-anmodningstypen

11
Klientens IP-adresse
0.0.0.0
Streng
4
Klientens IP-adresse (hvis der er nogen)

15
Din klient IP-adresse
172.16.134.61
Streng
4
IP-adresse foreslået af serveren (hvis der er nogen)

19
Næste servers IP-adresse
0.0.0.0
Streng
4
Serverens IP-adresse (hvis kendt)

23
Relay agent IP-adresse
172.16.114.41
Streng
4
IP-adressen på relay-agent (f.eks. switch)

27
Klientens MAC-adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-adressen på afsenderen af pakken (klienten)

31
Klientens hardwareadresse padding
 
Hex
10
Reserveret plads. Normalt fyldt med nuller

41
Serverens vært navn
 
Streng
64
Navn på DHCP-serveren. Normalt ikke sendt

105
Boot-filnavn
 
Streng
128
Filnavn på serveren, der bruges af diskfrie stationer under opstart

235
Magic cookie
63:82:53:63
Hex
4
«Magisk» nummer, som blandt andet bruges til at identificere, at denne pakke tilhører DHCP-protokollen

DHCP-muligheder. De kan komme i vilkårlig rækkefølge

236
Mulighedens nummer
53
Dec
1
Mulighed 53, der definerer typen af DHCP-pakke 2 — DHCPOFFER

 
Mulighedens længde
1
Dec
1

 
Mulighedens værdi
2
Dec
1

 
Mulighedens nummer
1
Dec
1
Mulighed, der tilbyder DHCP-klienten netværksmasken

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
255.255.224.0
Streng
4

 
Mulighedens nummer
3
Dec
1
Mulighed, der tilbyder DHCP-klienten standardgatewayen

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
172.16.12.1
Streng
4

 
Mulighedens nummer
6
Dec
1
Mulighed, der tilbyder DHCP-klienten DNS

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
8.8.8.8
Streng
4

 
Mulighedens nummer
51
Dec
1
Levetid for de udstedte netværksparametre i sekunder, hvorefter DHCP-klienten skal anmode om dem igen

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
86400
Dec
4

 
Mulighedens nummer
82
Dec
1
Mulighed 82, gentager det, der kom i DHCPDISCOVER

 
Mulighedens længde
18
Dec
1

 
Mulighedens værdi
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Pakkeafslutning
255
Dec
1
255 symboliserer afslutningen på pakken

DHCPREQUEST

Når kunden modtager DHCPOFFER, danner de en pakke med en anmodning om netværksparametre, ikke til alle DHCP-servere i netværket, men kun til en specifik, hvis DHCPOFFER-tilbud de fandt mest "tiltalende". Kriterierne for "tiltalende" kan være forskellige og afhænger af implementeringen af DHCP-klienten. Modtageren af anmodningen angives ved hjælp af MAC-adressen på DHCP-serveren. Derudover kan DHCPREQUEST-pakken sendes af klienten uden at fremstille tidligere DHCPDISCOVER, hvis IP-adressen fra serveren tidligere allerede var modtaget.

Tabel for strukturen af pakken DHCPREQUEST

Position i pakken
Navnet på værdien (almindelig brugt)
Eksempel
booking
Byte
Forklaring

1
Boot Request
1
Hex
1
Meddelelsestype. 1 — anmodning fra klient til server, 2 — svar fra server til klient

2
Hardware type
1
Hex
1
Type af hardwareadresse; i dette protokol 1 — MAC

3
Hardware adresses længde
6
Hex
1
Længden af enhedens MAC-adresse

4
Hops
1
Hex
1
Antal mellemruter

5
Transaktions-id
23:cf:de:1d
Hex
4
Unik identifikator for transaktionen. Genereres af klienten i begyndelsen af anmodningsoperationen

7
Sekunder forløbet
0
Hex
4
Tiden i sekunder siden starten af processen til at få adressen

9
Bootp flag
8000
Hex
2
Nogle flag, der kan sættes til at angive protokolparametre. I dette tilfælde er "broadcast" angivet

11
Klientens IP-adresse
0.0.0.0
Streng
4
Klientens IP-adresse (hvis der er nogen)

15
Din klient IP-adresse
172.16.134.61
Streng
4
IP-adresse foreslået af serveren (hvis der er nogen)

19
Næste servers IP-adresse
0.0.0.0
Streng
4
Serverens IP-adresse (hvis kendt)

23
Relay agent IP-adresse
172.16.114.41
Streng
4
IP-adressen på relay-agent (f.eks. switch)

27
Klientens MAC-adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-adressen på afsenderen af pakken (klienten)

31
Klientens hardwareadresse padding
 
Hex
10
Reserveret plads. Normalt fyldt med nuller

41
Serverens vært navn
 
Streng
64
Navn på DHCP-serveren. Normalt ikke sendt

105
Boot-filnavn
 
Streng
128
Filnavn på serveren, der bruges af diskfrie stationer under opstart

235
Magic cookie
63:82:53:63
Hex
4
«Magisk» nummer, som blandt andet bruges til at identificere, at denne pakke tilhører DHCP-protokollen

DHCP-muligheder. De kan komme i vilkårlig rækkefølge

236
Mulighedens nummer
53
Dec
3
Mulighed 53, der definerer typen af DHCP-pakken 3 — DHCPREQUEST

 
Mulighedens længde
1
Dec
1

 
Mulighedens værdi
3
Dec
1

 
Mulighedens nummer
61
Dec
1
Klientidentifikator: 01 (for Ethernet) + MAC-adressen for klienten

 
Mulighedens længde
7
Dec
1

 
Mulighedens værdi
01:2c:ab:25:ff:72:a6
Hex
7

 
Mulighedens nummer
60
Dec
 
"Vendor class identifier". I mit tilfælde angiver det versionen af DHCP-klienten. Måske returnerer andre enheder noget andet. Windows for eksempel rapporterer MSFT 5.0

 
Mulighedens længde
11
Dec
 

 
Mulighedens værdi
udhcp 0.9.8
Streng
 

 
Mulighedens nummer
55
 
1
Netværksparametre, som klienten anmoder om. Indholdet kan variere

01 — Netværksmaske
03 — Gateway
06 — DNS
oc — Værtsnavn
0f — netværksdomænenavn
1c — adresse til broadcast-anmodning
42 — TFTP-serverens navn
79 — Classless Static Route

 
Mulighedens længde
8
 
1

 
Mulighedens værdi
01:03:06:0c:0f:1c:42:79
 
8

 
Mulighedens nummer
82
Dec
1
Mulighed 82, gentager det, der kom i DHCPDISCOVER

 
Mulighedens længde
18
Dec
1

 
Mulighedens værdi
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Pakkeafslutning
255
Dec
1
255 symboliserer afslutningen på pakken

DHCPACK

Som bekræftelse af, at "ja, det er helt korrekt, dette er din IP-adresse, og jeg vil ikke give den til nogen andre", sendes pakken i DHCPACK-format fra DHCP-serveren til klienten. Den sendes også som de andre pakker via multicast. Selvom der i koden til DHCP-serveren, der er implementeret i Python, for en sikkerheds skyld duplicerer enhver multicast-anmodning ved at sende en pakke til en specifik IP for klienten, hvis den allerede er kendt. DHCP-serveren er helt ligeglad med, om DHCPACK-pakken er nået frem til klienten. Hvis klienten ikke modtager DHCPACK, gentager de bare DHCPREQUEST efter et stykke tid.

Tabel for strukturen af pakken DHCPACK

Position i pakken
Navnet på værdien (almindelig brugt)
Eksempel
booking
Byte
Forklaring

1
Boot Request
2
Hex
1
Meddelelsestype. 1 — anmodning fra klient til server, 2 — svar fra server til klient

2
Hardware type
1
Hex
1
Type af hardwareadresse; i dette protokol 1 — MAC

3
Hardware adresses længde
6
Hex
1
Længden af enhedens MAC-adresse

4
Hops
1
Hex
1
Antal mellemruter

5
Transaktions-id
23:cf:de:1d
Hex
4
Unik identifikator for transaktionen. Genereres af klienten i begyndelsen af anmodningsoperationen

7
Sekunder forløbet
0
Hex
4
Tiden i sekunder siden starten af processen til at få adressen

9
Bootp flag
8000
Hex
2
Nogle flag, der kan sættes til at angive protokolparametre. I dette tilfælde er "broadcast" angivet

11
Klientens IP-adresse
0.0.0.0
Streng
4
Klientens IP-adresse (hvis der er nogen)

15
Din klient IP-adresse
172.16.134.61
Streng
4
IP-adresse foreslået af serveren (hvis der er nogen)

19
Næste servers IP-adresse
0.0.0.0
Streng
4
Serverens IP-adresse (hvis kendt)

23
Relay agent IP-adresse
172.16.114.41
Streng
4
IP-adressen på relay-agent (f.eks. switch)

27
Klientens MAC-adresse
14:d6:4d:a7:c9:55
Hex
6
MAC-adressen på afsenderen af pakken (klienten)

31
Klientens hardwareadresse padding
 
Hex
10
Reserveret plads. Normalt fyldt med nuller

41
Serverens vært navn
 
Streng
64
Navn på DHCP-serveren. Normalt ikke sendt

105
Boot-filnavn
 
Streng
128
Filnavn på serveren, der bruges af diskfrie stationer under opstart

235
Magic cookie
63:82:53:63
Hex
4
«Magisk» nummer, som blandt andet bruges til at identificere, at denne pakke tilhører DHCP-protokollen

DHCP-muligheder. De kan komme i vilkårlig rækkefølge

236
Mulighedens nummer
53
Dec
3
Mulighed 53, der definerer typen af DHCP-pakken 5 — DHCPACK

 
Mulighedens længde
1
Dec
1

 
Mulighedens værdi
5
Dec
1

 
Mulighedens nummer
1
Dec
1
Mulighed, der tilbyder DHCP-klienten netværksmasken

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
255.255.224.0
Streng
4

 
Mulighedens nummer
3
Dec
1
Mulighed, der tilbyder DHCP-klienten standardgatewayen

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
172.16.12.1
Streng
4

 
Mulighedens nummer
6
Dec
1
Mulighed, der tilbyder DHCP-klienten DNS

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
8.8.8.8
Streng
4

 
Mulighedens nummer
51
Dec
1
Levetid for de udstedte netværksparametre i sekunder, hvorefter DHCP-klienten skal anmode om dem igen

 
Mulighedens længde
4
Dec
1

 
Mulighedens værdi
86400
Dec
4

 
Mulighedens nummer
82
Dec
1
Mulighed 82, gentager det, der kom i DHCPDISCOVER

 
Mulighedens længde
18
Dec
1

 
Mulighedens værdi
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Pakkeafslutning
255
Dec
1
255 symboliserer afslutningen på pakken

Installation

Opsætningen består i at installere de Python-moduler, der er nødvendige for at fungere. Det forudsættes, at MySQL allerede er installeret og konfigureret.

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

Opretter en MySQL-database, uploader dumpen pydhcp.sql til den, konfigurerer konfigurationsfilen.

Konfiguration

Alle serverindstillinger ligger i en XML-format fil. Referencefilen:

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

Lad os se nærmere på tags:

Sektionen dhcpserver beskriver de grundlæggende indstillinger for at starte serveren, nemlig:

  • host — hvilken IP-adresse serveren lytter på port 67
  • broadcast — hvilken IP der er broadcast for DHCPOFFER og DHCPACK
  • DHCPServer — hvilken IP adresse DHCP serveren har
  • LeaseTime — lejeperioden for den tildelte IP-adresse
  • ThreadLimit — hvor mange samtidige tråde der er i gang med at behandle indkomne UDP-pakker på port 67. Det forventes at hjælpe på højbelastede projekter 😉
  • defaultMask, defaultRouter, defaultDNS — de værdier der tilbydes abonnenten som standard, hvis IP'en findes i databasen, men der ikke er angivet yderligere parametre for denne

Sektionen mysql:

host, username, password, basename — alt taler for sig selv. En omtrentlig struktur af databasen er beskrevet på GitHub

Sektionen query: her beskrives forespørgslerne til at få OFFER/ACK:

  • offer_count — antallet af rækker med forespørgsler der returnerer resultater af typen ip, mask, router, dns
  • offer_n — rækkeforespørgslen. Hvis returværdien er tom, udfører den den næste forespørgsel offer
  • history_sql — forespørgsel, der f.eks. skriver i "autorisationshistorien" for brugeren

I forespørgslerne kan der deltage enhver variabel fra sektionen options eller muligheder fra DHCP-protokollen.

Sektionen options. Her bliver det interessant. Her kan vi oprette variabler, som vi kan bruge senere i sektionen query.

For eksempel:

option_82_hex:sw_port1:20:22

, denne kommando tager hele strengen fra DHCP-anmodningen i option 82, i hex-format, fra byte 20 til 22 inklusive, og gemmer den i en ny variabel sw_port1 (porten på switchen, hvor anmodningen kom fra)

option_82_hex:sw_mac:26:40

, vi definerer variablen sw_mac, ved at tage hex fra intervallet 26:40

For at se alle mulige optioner, der kan bruges i forespørgsler, kan man starte serveren med nøgle -d. Vi vil se en log, der ligner følgende:

--pakke modtaget DHCPINFORM på port 67, fra 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'}

Derfor kan vi indkapsle enhver variabel i {} og den vil blive brugt i SQL-anmodningen.

Lad os notere, at IP-adressen, klienten fik:

DHCP+Mysql server på Python

DHCP+Mysql server på Python

Start af server

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

— d tilstand for debug-output til konsollen
— c <fil_navn> konfigurationsfil

Analyse af resultater

Lad os nu se nærmere på implementeringen af serveren i Python. Det er en udfordring. Python blev lært "on-the-go". Mange dele er lavet i stilen: "wow, jeg gjorde det, og det virker". Slet ikke optimeret, og er efterladt i denne tilstand primært på grund af lille erfaring med udvikling i Python. Jeg vil fokusere på de mest interessante aspekter af implementeringen af serveren i "koden".

Parser til XML-konfigurationsfilen

Den standard Python-modul xml.dom anvendes. Det virker ret enkelt, men under implementeringen manglede jeg klart dokumentation og gode eksempler online, når man arbejdede med dette modul.

    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

Under surprisingly simple implementation, multithreading in Python is quite straightforward.

def PacketWork(data, addr): 
...
# implementation for parsing the incoming packet and responding to it
...


while True:
    data, addr = udp_socket.recvfrom(1024) # waiting for UDP packet
    thread = threading.Thread(target=PacketWork, args=(data, addr,)).start()  # as soon as it arrives, we start the previously defined PacketWork function in the background with parameters
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # if the number of active threads exceeds the limit set in the configuration, we wait until it decreases

Receiving/Sending DHCP Packets

To intercept UDP packets traveling through the network card, you need to "raise" the socket:

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

, where flags:

  • AF_INET means that the address format will be IP: port. It can also be AF_UNIX, where the address is specified by the filename.
  • SOCK_DGRAM betyder, at vi modtager ikke en "rå pakke", men en der allerede har passeret firewallen, og med en delvist trimmed pakke. Dvs. vi får kun UDP-pakken uden den "fysiske" komponent af UDP-pakkens indpakning. Hvis vi bruger flaget SOCK_RAW, så skal vi også parse denne "indpakning".

Afsendelse af pakken kan være enten som broadcast:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # skifter socket til broadcast-funktionsmode
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, såvel som til den adresse, "hvor pakken kom fra":

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # skifter socket til "flere lyttere"
                        rz=udp_socket.sendto(packetack, addr)

, hvor SOL_SOCKET betyder "protokolniveau" til indstilling af muligheder,

, SO_BROADCAST er muligheden for, at pakken sendes som "broadcast".

  , SO_REUSEADDR er en mulighed, der skifter socket til "flere lyttere". Teoretisk set er det ikke nødvendigt i dette tilfælde, men på en af de FreeBSD-servere, jeg testede på, fungerede koden ikke uden denne mulighed.

Analyse af DHCP-pakken

Her blev jeg virkelig imponeret over Python. Det viser sig, at det i "boksen" tillader en temmelig fri håndtering af byte-koden. Det giver mulighed for meget let at konvertere det til decimale værdier, strenge og hex – dvs. det, vi faktisk har brug for for at forstå strukturpakken. For eksempel kan man få et interval af bytes i HEX og bare bytes:

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

, pakke bytes ind i en struktur:

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

Få IP fra strukturen:

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

Og omvendt:

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

Det var alt 😉

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster