
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 (min fork), originalen , 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 .
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:
- 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.
- 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.
- 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.
- Serveren, der modtog DHCPREQUEST, sender en pakke i formatet DHCPACK, som endnu en gang opremser netværksindstillingerne, der er beregnet til denne klient.

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:
![]()
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å
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:


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 decreasesReceiving/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
