
Cilj ovog projekta bio je:
- Istraživanje DHCP protokola u IPv4 mreži
- Učenje Pythona (malo više od nule 😉 )
- zamena servera (moj fork), original , koji postaje sve teže kompilirati za novi OS. I ne sviđa mi se što binarni fajl koji ne mogu "promeniti odmah"
- dobijanje funkcionalnog DHCP servera sa mogućnošću dobijanja IP adrese klijenta po MAC adresi klijenta ili kombinaciji MAC switcha + port (Option 82)
- pisanje još jednog bicikla (O! to je moja omiljena aktivnost)
- dobijanje kritika za svoju nespretnost na HabrHabr (ili bolje pozivnicu) 😉
Rezultat: radi 😉 Isprobano na FreeBSD i Ubuntu OS-u. Teoretski, kod se može prilagoditi da radi na bilo kojem OS-u, jer u kodu kao da nema specifičnih zaleđivanja.
Pažnja! Dalje sledi puno.
Link na repozitorij za ljubitelje .
Proces instalacije, podešavanja i korišćenja rezultata «istraživanja» je mnogo niže, a zatim malo teorije o DHCP protokolu. Za sebe. I za istoriju 😉
Malo teorije
Šta je DHCP
To je mrežni protokol koji omogućava uređaju da sazna svoju IP adresu (kao i druge parametre poput prolaza, DNS-a i slično) od DHCP servera. Razmena paketa se odvija putem UDP protokola. Opšti princip rada uređaja prilikom zahteva za mrežne parametre je sledeći:
- Uređaj (klijent) šalje rasprostranjen UDP zahtev (DHCPDISCOVER) širom mreže sa zahtevom "pa neko, dajte mi IP adresu". Obično (ali ne uvek) zahtev dolazi sa porta 68 (izvor), a odredišni port je 67. Neki uređaji šalju pakete i sa porta 67. U paketu DHCPDISCOVER uključena je MAC adresa uređaja klijenta.
- Svi DHCP serveri koji se nalaze u mreži (a može ih biti više) formiraju DHCPOFFER ponudu sa mrežnim podešavanjima za uređaj koji je poslao DHCPDISCOVER, i takođe je široko šalju kroz mrežu. Identifikacija kome je paket namenjen ide prema MAC adresi klijenta, koja je prethodno pružena u zahtevu DHCPDISCOVER.
- Stranka strankama prodaje DNS usluge, odabire najprivlačniju (kriteriji mogu biti različiti, na primjer vrijeme isporuke paketa, broj prolaznih ruta) i šalje službeni zahtjev DHCPREQUEST s mrežnim postavkama serveru koji mu se svidio. U tom slučaju paket ide konkretnom DHCP serveru.
- Server koji primi DHCPREQUEST šalje paket formata DHCPACK, u kojem još jednom navodi mrežne postavke namijenjene tom klijentu.

Osim toga, postoje paketi DHCPINFORM koji se šalju od klijenta i čija je svrha obavijestiti DHCP server da je "klijent živ" i koristi dodijeljene mrežne postavke. Ovi paketi se u implementaciji ovog servera ignoriraju.
Format paketa
U cjelini, okvir Ethernet paketa izgleda otprilike ovako:
![]()
U našem slučaju razmatraćemo samo podatke unutar paketa UDP, bez zaglavlja protokola razina OSI, a to je struktura DHCP:
DHCPDISCOVER
Dakle, proces dobivanja IP adrese za uređaj započinje tako što DHCP klijent šalje multicast upit s porta 68 na 255.255.255.255:67. U ovom paketu klijent uključuje svoju MAC adresu, kao i ono što zapravo želi dobiti od DHCP servera. Struktura paketa opisana je u tabeli ispod.
Tabela strukture paketa DHCPDISCOVER
Pozicija u paketu
Naziv vrijednosti
Primer
Prikaz
Bajt
Objašnjenje
1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu
2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj prolaznih ruta
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva
7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp flags
0
Hex
2
Neki flagevi koji se mogu postaviti kao naznaka parametara protokola
11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)
15
Your client IP address
0.0.0.0
Vrstica
4
IP adresa predložena od strane servera (ako postoji)
19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)
23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Client hardware address padding
Hex
10
Rezervirano mjesto. Obično popunjeno nulama
41
Server host name
Vrstica
64
Naziv DHCP servera. Obično se ne šalje
105
Boot file name
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju
235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP
DHCP možnosti. Lahko so v katerem koli vrstnem redu
236
Številka možnosti
53
Dec
1
Možnost 53, ki določa vrsto DHCP paketa
1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM
Dolžina možnosti
1
Dec
1
Vrednost možnosti
1
Dec
1
Številka možnosti
50
Dec
1
Kakšen IP naslov želi prejeti odjemalec
Dolžina možnosti
4
Dec
1
Vrednost možnosti
172.16.134.61
Vrstica
4
Številka možnosti
55
1
Zahtevane omrežne nastavitve odjemalca. Sestava je lahko različna
01 — Maska omrežja
03 — Prehod
06 — DNS
oc — Ime gostitelja
0f — ime domene omrežja
1c — naslov za širokoustni (broadcast) poletje
42 — ime TFTP strežnika
79 — Classless Static Route
Dolžina možnosti
8
1
Vrednost možnosti
01:03:06:0c:0f:1c:42:79
8
Številka možnosti
82
Dec
Možnost 82, ki prenaša MAC naslov naprave — retransponderja in nekatera dodatna vrednost.
Najpogosteje — port stikala, na katerem deluje končni DHCP odjemalec. V tej možnosti so 'vključene' dodatne nastavitve. Prvi bajt je številka 'podmožnosti', drugi je njena dolžina, nato pa njena vrednost.
V tem primeru so v možnosti 82 vključene podmožnosti:
Agent Circuit ID = 00:04:00:01:00:04, kjer sta zadnja dva bajta port odjemalca DHCP, s katerega je prišel zahtevek
Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC naslov naprave retransponderja DHCP
Dolžina možnosti
18
Dec
Vrednost možnosti
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
Konec paketa
255
Dec
1
255 simbolizira konec paketa
DHCPOFFER
Ko strežnik prejme paket DHCPDISCOVER in vidi, da lahko odjemalcu ponudi nekaj iz zahtevanega, oblikuje odgovor — DHCPDISCOVER. Odgovor se pošlje na port 'od koder je prišel', po širokoustnem načinu, saj v tem trenutku odjemalec še nima IP naslova, zato lahko paket sprejme le, če je poslan širokoustno. Odjemalec prepozna, da je to paket zanj po svojem MAC naslovu znotraj paketa, pa tudi po številu transakcije, ki ga generira ob oblikovanju prvega paketa.
Tabela strukture paketa DHCPOFFER
Pozicija u paketu
Ime vrednosti (splošno sprejeto)
Primer
Prikaz
Bajt
Objašnjenje
1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu
2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj prolaznih ruta
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva
7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp flags
0
Hex
2
Nekateri zastavice, ki jih je mogoče nastaviti kot označitev parametrov protokola. V tem primeru, 0 — pomeni vrsto zahteve Unicast
11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)
15
Your client IP address
172.16.134.61
Vrstica
4
IP adresa predložena od strane servera (ako postoji)
19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)
23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Client hardware address padding
Hex
10
Rezervirano mjesto. Obično popunjeno nulama
41
Server host name
Vrstica
64
Naziv DHCP servera. Obično se ne šalje
105
Boot file name
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju
235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP
DHCP možnosti. Lahko so v katerem koli vrstnem redu
236
Številka možnosti
53
Dec
1
Možnost 53, ki določa vrsto DHCP paketa 2 — DHCPOFFER
Dolžina možnosti
1
Dec
1
Vrednost možnosti
2
Dec
1
Številka možnosti
1
Dec
1
Možnost, ki ponuja DHCP odjemalcu masko omrežja
Dolžina možnosti
4
Dec
1
Vrednost možnosti
255.255.224.0
Vrstica
4
Številka možnosti
3
Dec
1
Možnost, ki ponuja DHCP odjemalcu privzeti prehod
Dolžina možnosti
4
Dec
1
Vrednost možnosti
172.16.12.1
Vrstica
4
Številka možnosti
6
Dec
1
Možnost, ki ponuja DHCP odjemalcu DNS
Dolžina možnosti
4
Dec
1
Vrednost možnosti
8.8.8.8
Vrstica
4
Številka možnosti
51
Dec
1
Časovna doba, v kateri so izdane omrežne nastavitve v sekundah, po kateri mora DHCP odjemalec ponovno zahtevati
Dolžina možnosti
4
Dec
1
Vrednost možnosti
86400
Dec
4
Številka možnosti
82
Dec
1
Možnost 82 ponavlja to, kar je prišlo v DHCPDISCOVER
Dolžina možnosti
18
Dec
1
Vrednost možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Konec paketa
255
Dec
1
255 simbolizira konec paketa
DHCPREQUEST
Ko klient prejme DHCPOFFER, oblikuje paket z zahtevo za omrežne parametre, ne več na vse DHCP strežnike v omrežju, temveč le na enega določenega, katerega DHCPOFFER se mu je najbolj "zdela". Kriteriji "zdela" so lahko različni in so odvisni od implementacije DHCP odjemalca. Prejemnika zahtevka določa MAC naslov DHCP strežnika. Paket DHCPREQUEST lahko odjemalec pošlje tudi brez predhodne oblikovanja DHCPDISCOVER, če je IP naslov strežnika že bil pridobljen prej.
Tabela strukture paketa DHCPREQUEST
Pozicija u paketu
Ime vrednosti (splošno sprejeto)
Primer
Prikaz
Bajt
Objašnjenje
1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu
2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj prolaznih ruta
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva
7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp flags
8000
Hex
2
Nekateri zastavice, ki se lahko nastavijo kot indikator parametrov protokola. V tem primeru je nastavljeno "broadcast".
11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)
15
Your client IP address
172.16.134.61
Vrstica
4
IP adresa predložena od strane servera (ako postoji)
19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)
23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Client hardware address padding
Hex
10
Rezervirano mjesto. Obično popunjeno nulama
41
Server host name
Vrstica
64
Naziv DHCP servera. Obično se ne šalje
105
Boot file name
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju
235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP
DHCP možnosti. Lahko so v katerem koli vrstnem redu
236
Številka možnosti
53
Dec
3
Možnost 53, ki določa tip paketa DHCP 3 — DHCPREQUEST
Dolžina možnosti
1
Dec
1
Vrednost možnosti
3
Dec
1
Številka možnosti
61
Dec
1
ID odjemalca: 01 (za Ethernet) + MAC naslov odjemalca
Dolžina možnosti
7
Dec
1
Vrednost možnosti
01:2c:ab:25:ff:72:a6
Hex
7
Številka možnosti
60
Dec
"Vendor class identifier". V mojem primeru sporoča verzijo DHCP odjemalca. Morda druge naprave vračajo nekaj drugega. Windows na primer sporoča MSFT 5.0.
Dolžina možnosti
11
Dec
Vrednost možnosti
udhcp 0.9.8
Vrstica
Številka možnosti
55
1
Zahtevane omrežne nastavitve odjemalca. Sestava je lahko različna
01 — Maska omrežja
03 — Prehod
06 — DNS
oc — Ime gostitelja
0f — ime domene omrežja
1c — naslov za širokoustni (broadcast) poletje
42 — ime TFTP strežnika
79 — Classless Static Route
Dolžina možnosti
8
1
Vrednost možnosti
01:03:06:0c:0f:1c:42:79
8
Številka možnosti
82
Dec
1
Možnost 82 ponavlja to, kar je prišlo v DHCPDISCOVER
Dolžina možnosti
18
Dec
1
Vrednost možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Konec paketa
255
Dec
1
255 simbolizira konec paketa
DHCPACK
Kot potrditev, da "da, to je tvoj IP naslov in ga nikomur drugemu ne bom dodelil" od DHCP strežnika, služi paket v formatu DHCPACK od strežnika k odjemalcu. Tako kot ostali paketi se tudi ta pošilja široko. V spodnjem kodi DHCP strežnika, implementiranega v Pythonu, v vsakem primeru podvojim katero koli širokoodmorničko zahtevo, tako da pošljem paket na določen IP odjemalca, če je ta že poznan. Pri tem DHCP strežnik sploh ne zanima, ali je paket DHCPACK prišel do odjemalca. Če odjemalec ne prejme DHCPACK, bo po nekem času preprosto ponovil DHCPREQUEST.
Tabela strukture paketa DHCPACK
Pozicija u paketu
Ime vrednosti (splošno sprejeto)
Primer
Prikaz
Bajt
Objašnjenje
1
Boot Request
2
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu
2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj prolaznih ruta
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva
7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp flags
8000
Hex
2
Nekateri zastavice, ki se lahko nastavijo kot indikator parametrov protokola. V tem primeru je nastavljeno "broadcast".
11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)
15
Your client IP address
172.16.134.61
Vrstica
4
IP adresa predložena od strane servera (ako postoji)
19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)
23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Client hardware address padding
Hex
10
Rezervirano mjesto. Obično popunjeno nulama
41
Server host name
Vrstica
64
Naziv DHCP servera. Obično se ne šalje
105
Boot file name
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju
235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP
DHCP možnosti. Lahko so v katerem koli vrstnem redu
236
Številka možnosti
53
Dec
3
Možnost 53, ki določa tip paketa DHCP 5 — DHCPACK
Dolžina možnosti
1
Dec
1
Vrednost možnosti
5
Dec
1
Številka možnosti
1
Dec
1
Možnost, ki ponuja DHCP odjemalcu masko omrežja
Dolžina možnosti
4
Dec
1
Vrednost možnosti
255.255.224.0
Vrstica
4
Številka možnosti
3
Dec
1
Možnost, ki ponuja DHCP odjemalcu privzeti prehod
Dolžina možnosti
4
Dec
1
Vrednost možnosti
172.16.12.1
Vrstica
4
Številka možnosti
6
Dec
1
Možnost, ki ponuja DHCP odjemalcu DNS
Dolžina možnosti
4
Dec
1
Vrednost možnosti
8.8.8.8
Vrstica
4
Številka možnosti
51
Dec
1
Časovna doba, v kateri so izdane omrežne nastavitve v sekundah, po kateri mora DHCP odjemalec ponovno zahtevati
Dolžina možnosti
4
Dec
1
Vrednost možnosti
86400
Dec
4
Številka možnosti
82
Dec
1
Možnost 82 ponavlja to, kar je prišlo v DHCPDISCOVER
Dolžina možnosti
18
Dec
1
Vrednost možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Konec paketa
255
Dec
1
255 simbolizira konec paketa
Namestitev
Namestitev v bistvu vključuje namestitev potrebnih Python modulov za delovanje. Predpostavlja se, da je MySQL že nameščen in nastavljen.
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
Ustvarimo MySQL bazo podatkov, naložimo vanjo dump pydhcp.sql, nastavimo konfiguracijsko datoteko.
Конфигурация
Vse nastavitve strežnika so v datoteki v formatu xml. Referenčna datoteka:
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')Zdajmo podrobneje o oznakah:
Odsek dhcpserver opisuje osnovne nastavitve za zagon strežnika, in sicer:
- host — kateri IP naslov strežnik posluša na priključku 67
- broadcast — kateri IP je broadcast za DHCPOFFER in DHCPACK
- DHCPServer — kateri IP ima DHCP strežnik
- LeaseTime — čas najema dodeljenega IP naslova
- ThreadLimit — koliko hkratnih niti je zagnanih za obdelavo prejetih UDP paketov na priključku 67. Predvideva se, da bo to pomagalo pri projektih z visoko obremenitvijo 😉
- defaultMask, defaultRouter, defaultDNS — to, kar se ponudi naročniku privzeto, če je IP najden v bazi, vendar dodatni parametri zanj niso navedeni
Odsek mysql:
host, username, password, basename — vse govori sama zase. Približna struktura podatkovne baze je objavljena na
Odsek query: tukaj so opisani poizvedbi za pridobitev OFFER/ACK:
- offer_count — število vrstic z poizvedbami, ki vračajo rezultat v obliki ip, mask, router, dns
- offer_n — vrstica poizvedbe. Če je vrnitev prazna, izvede naslednjo poizvedbo offer
- history_sql — poizvedba, ki na primer piše v "zgodovino avtorizacije" naročnika
V poizvedbah lahko sodelujejo poljubne spremenljivke iz odseka options ali možnosti iz protokola DHCP.
Odsek options. Tukaj je že bolj zanimivo. Tu lahko ustvarjamo spremenljivke, ki jih lahko uporabljamo kasneje v odseku query.
Na primer:
option_82_hex:sw_port1:20:22, ta ukazova komanda vzima vse stringi prišle v DHCP zahtevi, v formatu hex, v razponu od 20 do 22 bajtov vključno in jo postavi v novo spremenljivko sw_port1 (priključek stikala, od koder je prišla zahteva)
option_82_hex:sw_mac:26:40, določamo spremenljivko sw_mac, tako da vzamemo hex iz razpona 26:40
Vse možne možnosti, ki jih lahko uporabimo v zahtevah, lahko vidimo tako, da zaženemo strežnik s ključem -d. Videli bomo približno takšen log:
--pršel je paket DHCPINFORM na vratih 67, od 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'}Zato lahko katero koli spremenljivko ovijemo v {} in bo uporabljena v SQL zahtevi.
Zabeležimo za zgodovino, da je odjemalec prejel IP naslov:


Zagon strežnika
.\/pydhcpdb.py -d -c config.xml
— d način prikaza v konzoli DEBUG
— c <ime_datoteke> konfiguracijska datoteka
Analiza
Zdaj pa podrobneje o implementaciji strežnika v Pythonu. To je bolečina. Python je bil učen 'v teku'. Mnogi trenutki so bili narejeni v stilu: 'uhu, nekako sem naredil, da deluje'. Sploh niso optimizirani in so ostali v tem stanju predvsem zaradi malo izkušenj v razvoju na Pythonu. Osredotočil se bom na najbolj zanimive trenutke implementacije strežnika v 'kodu'.
Parser za datoteko z nastavitvami XML
Uporablja se standardni modul Python xml.dom. Zdi se preprosto, vendar pri implementaciji močno primanjkuje kakovostne dokumentacije in primerov na spletu, ki bi uporabljali ta 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)Večnitnost
Необычно, но многопоточность в Python реализована достаточно просто и понятно.
def PacketWork(data, addr):
...
# реализация разбора пришедшего пакета и ответа на него
...
while True:
data, addr = udp_socket.recvfrom(1024) # ждем пакет UDP
thread = threading.Thread(target=PacketWork, args=(data, addr,)).start() # как только пришел, запускаем в фоне заранее определенную функцию PacketWork с параметрами
while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
time.sleep(1) # если количество запущенных потоков превышает настройку, ждем, пока их станет меньшеПрием/отправка пакета DHCP
Чтобы перехватить UDP-пакеты, проходящие через сетевую карту, нужно «поднять» сокет:
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"], 67))
, где флаги:
- AF_INET — обозначает, что формат адреса будет IP:порт. Существуют также AF_UNIX — где адрес задается именем файла.
- SOCK_DGRAM — означает, что мы принимаем не «сырой пакет», а уже прошедший через файрвол, и с частично обрезанным пакетом. То есть, мы получаем только UDP-пакет без «физической» части обертки пакета UDP. Если использовать флаг SOCK_RAW, то придется также парсить эту «обертку».
Pošiljanje paketa je lahko bodisi multicast:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # preklopimo soket v način pošiljanja multicast
rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68)), bodisi na naslov, «od koder je prišel paket»:
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # preklopimo soket v način "več poslušalcev"
rz=udp_socket.sendto(packetack, addr), kjer SOL_SOCKET pomeni «nivo protokola» za nastavitev možnosti,
, SO_BROADCAST možnost, ki pošilja paket «multicast»
, SO_REUSEADDR možnost, ki soket preklopi v način «več poslušalcev». V teoriji ni potrebna v tem primeru, vendar na eni od strežnikov FreeBSD, kjer sem izvajal teste, brez te možnosti koda ni delovala.
Razčlenitev paketa DHCP
Tukaj me je Python resnično navdušil. Izkazalo se je, da omogoča precej prosto delo z byte-kodom. Omogoča zelo preprosto pretvorbo v decimalne vrednosti, nize in hex — torej to, kar pravzaprav potrebujemo, da razumemo strukturo paketa. Tako lahko na primer dobimo obseg byte-ov v HEX in preprostih byte-ih:
res["xidhex"]=data[4:8].hex()
res["xidbyte"]=data[4:8], zložiti byte-e v strukturo:
res["flags"]=pack('BB',data[10],data[11])Pridobiti IP iz strukture:
res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));In obratno:
res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])
To je to vse 😉
Izvor: habr.com
