
Cilj ovog projekta je bio:
- Istraživanje DHCP protokola u radu s IPv4 mrežom
- Istraživanje Pythona (malo više od nule 😉 )
- zamjena serveru (moj fork), original , koji se sve teže i teže sastavlja za novi operativni sustav. Također mi se ne sviđa što binarni program ne može 'promijeniti odmah'
- dobivanje funkcionalnog DHCP servera s mogućnošću odabira IP adrese klijenta prema MAC adresi klijenta ili kombinaciji MAC adrese switcha+port (Option 82)
- pisanje još jednog kotača (O! to je moja omiljena aktivnost)
- dobivanje kritika zbog svoje nespretnosti na Habrhabr (ili, još bolje, pozivnice) 😉
Rezultat: radi 😉 Isprobano na FreeBSD i Ubuntu OS-u. Teoretski, kod se može zamoliti da radi na bilo kojem operativnom sustavu, pošto nema specifičnih vezanosti u kodu.
Pažnja! Dalje slijedi puno.
Link na repozitorij za entuzijaste .
Proces instalacije, konfiguracije i korištenja rezultata 'istraživanja materije' slijedi niže, a zatim malo teorije o DHCP protokolu. Za sebe. I za povijest 😉
Malo teorije
Što je DHCP
To je mrežni protokol koji omogućava uređaju da sazna svoju IP adresu (i druge parametre kao što su gateway, DNS i slično) od DHCP servera. Razmjena paketa odvija se putem UDP protokola. Opći princip rada uređaja prilikom zahtjeva za mrežnim parametrima je sljedeći:
- Uređaj (klijent) šalje širokouništeni UDP zahtjev (DHCPDISCOVER) kroz cijelu mrežu s upitom 'neka netko da mi IP adresu'. Obično (ali ne uvijek) zahtjev dolazi s porta 68 (izvor), a odredišni port je 67. Neka uređaja također šalju pakete s porta 67. Unutar DHCPDISCOVER paketa uključen je MAC adresa uređaja klijenta.
- Svi DHCP serveri koji se nalaze u mreži (a može ih biti više) formiraju ponudu DHCPOFFER za uređaj koji je poslao DHCPDISCOVER, s mrežnim postavkama, i također ga širokouništeno šalju kroz mrežu. Identifikacija kome je ovaj paket namijenjen odvija se putem MAC adrese klijenta, pružene ranije u zahtjevu DHCPDISCOVER.
- Klijent prima pakete s ponudama mrežnih postavki, odabire najprivlačniji (kriteriji mogu biti različiti, na primjer, i prema vremenu isporuke paketa, broju međurute), i šalje 'službeni zahtjev' DHCPREQUEST na omiljenom DHCP poslužitelju sa mrežnim postavkama. U tom slučaju, paket ide već konkretnom DHCP poslužitelju.
- Poslužitelj koji je primio DHCPREQUEST šalje paket formata DHCPACK, u kojem ponovno navodi mrežne postavke namijenjene ovom klijentu.

Osim toga, postoje paketi DHCPINFORM koji dolaze od klijenta, a cilj im je obavijestiti DHCP poslužitelj da je 'klijent živ' i koristi dodijeljene mrežne postavke. U implementaciji ovog poslužitelja, ovi paketi se ignoriraju.
Format paketa
Općenito, okvir Ethernet paketa izgleda otprilike ovako:
![]()
U našem slučaju razmatramo samo podatke izravno sadržane u UDP paketu, bez zaglavlja protokola razina OSI, a to je struktura DHCP-a:
DHCPDISCOVER
Dakle, proces dobivanja IP adrese za uređaj započinje slanjem širokofrekventnog zahtjeva od strane DHCP klijenta s porta 68 na 255.255.255.255:67. U ovom paketu klijent uključuje svoju MAC adresu, kao i što točno želi dobiti od DHCP poslužitelja. Struktura paketa opisana je u tablici ispod.
Tablica strukture paketa DHCPDISCOVER
Pozicija u paketu
Naziv vrijednosti
Primjer
Prikaz
Bajt
Objašnjenje
1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema poslužitelju, 2 — odgovor od poslužitelja klijentu
2
Vrsta hardvera
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Duljina hardverske adrese
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj međurute
5
Transakcijski ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Klijent ga generira na početku operacije zahtjeva
7
Drugo proteklo vrijeme
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp zastavice
0
Hex
2
Neke zastavice koje se mogu postaviti kao oznaka za parametre protokola
11
IP adresa klijenta
0.0.0.0
Retak
4
IP adresa klijenta (ako postoji)
15
Vaša IP adresa klijenta
0.0.0.0
Retak
4
IP adresa ponuđena od strane poslužitelja (ako postoji)
19
IP adresa sljedećeg poslužitelja
0.0.0.0
Retak
4
IP adresa poslužitelja (ako je poznata)
23
IP adresa agenta preusmjeravanja
172.16.114.41
Retak
4
IP adresa agenta preusmjeravanja (na primjer, switch)
27
MAC adresa klijenta
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Puni sastav MAC adrese klijenta
Hex
10
Rezervirani prostor. Obično popunjen nulama
41
Ime poslužitelja
Retak
64
Ime DHCP poslužitelja. Obično se ne prenosi
105
Naziv boot datoteke
Retak
128
Naziv datoteke na poslužitelju koji se koristi od strane bezdiskovnih stanica prilikom podizanja.
235
Magic cookie
63:82:53:63
Hex
4
«Magični» broj, prema kojem se može odrediti pripada li ovaj paket DHCP protokolu.
DHCP opcije. Mogu se pojaviti u bilo kojem redoslijedu.
236
Broj opcije
53
Dec
1
Opcija 53, koja određuje tip DHCP paketa.
1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM
Duljina opcije
1
Dec
1
Vrijednost opcije
1
Dec
1
Broj opcije
50
Dec
1
Koju IP adresu želi dobiti klijent
Duljina opcije
4
Dec
1
Vrijednost opcije
172.16.134.61
Retak
4
Broj opcije
55
1
Zahtijevani mrežni parametri od strane klijenta. Sadržaj može biti različit.
01 — Maska podmreže
03 — Prolaz
06 — DNS
oc — Ime hosta
0f — ime domene mreže
1c — adresa multicast (broadcast) zahtjeva
42 — ime TFTP poslužitelja
79 — Classless Static Route
Duljina opcije
8
1
Vrijednost opcije
01:03:06:0c:0f:1c:42:79
8
Broj opcije
82
Dec
Opcija 82, u kojoj se prenosi MAC adresa uređaja — retransmitera i neka dodatna vrijednost.
Najčešće — port switch-a na kojem radi krajnji DHCP klijent. U ovoj opciji su "utemeljeni" dodatni parametri. Prvi bajt — broj "podopcije", drugi njen broj bajtova, zatim njezina vrijednost.
U ovom slučaju, u opciji 82 su uteljenjene podopcje:
Agent Circuit ID = 00:04:00:01:00:04, gdje zadnja dva bajta označavaju port DHCP klijenta s kojeg je došao zahtjev.
Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC adresa uređaja retransmitera DHCP.
Duljina opcije
18
Dec
Vrijednost opcije
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
Kraj paketa
255
Dec
1
255 označava kraj paketa.
DHCPOFFER
Čim poslužitelj primi paket DHCPDISCOVER i ako vidi da može ponuditi nešto klijentu iz traženog, formira odgovor — DHCPDISCOVER. Odgovor se šalje na port "odakle je došao", putem broadcasta, jer u tom trenutku klijent još nema IP adresu, a stoga paket može prihvatiti samo ako je poslan široko. Klijent prepoznaje da je to paket za njega prema svom MAC adresi unutar paketa, kao i prema broju transakcije koji generira pri stvaranju prvog paketa.
Tablica strukture paketa DHCPOFFER
Pozicija u paketu
Naziv vrijednosti (opće prihvaćeno)
Primjer
Prikaz
Bajt
Objašnjenje
1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema poslužitelju, 2 — odgovor od poslužitelja klijentu
2
Vrsta hardvera
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Duljina hardverske adrese
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj međurute
5
Transakcijski ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Klijent ga generira na početku operacije zahtjeva
7
Drugo proteklo vrijeme
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp zastavice
0
Hex
2
Neki zastavice koje se mogu postaviti kao naznaka protokolskih parametara. U ovom slučaju, 0 — označava tip zahtjeva Unicast.
11
IP adresa klijenta
0.0.0.0
Retak
4
IP adresa klijenta (ako postoji)
15
Vaša IP adresa klijenta
172.16.134.61
Retak
4
IP adresa ponuđena od strane poslužitelja (ako postoji)
19
IP adresa sljedećeg poslužitelja
0.0.0.0
Retak
4
IP adresa poslužitelja (ako je poznata)
23
IP adresa agenta preusmjeravanja
172.16.114.41
Retak
4
IP adresa agenta preusmjeravanja (na primjer, switch)
27
MAC adresa klijenta
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Puni sastav MAC adrese klijenta
Hex
10
Rezervirani prostor. Obično popunjen nulama
41
Ime poslužitelja
Retak
64
Ime DHCP poslužitelja. Obično se ne prenosi
105
Naziv boot datoteke
Retak
128
Naziv datoteke na poslužitelju koji se koristi od strane bezdiskovnih stanica prilikom podizanja.
235
Magic cookie
63:82:53:63
Hex
4
«Magični» broj, prema kojem se može odrediti pripada li ovaj paket DHCP protokolu.
DHCP opcije. Mogu se pojaviti u bilo kojem redoslijedu.
236
Broj opcije
53
Dec
1
Opcija 53, koja određuje tip DHCP paketa 2 — DHCPOFFER.
Duljina opcije
1
Dec
1
Vrijednost opcije
2
Dec
1
Broj opcije
1
Dec
1
Opcija koja nudi DHCP klijentu masku podmreže.
Duljina opcije
4
Dec
1
Vrijednost opcije
255.255.224.0
Retak
4
Broj opcije
3
Dec
1
Opcija koja nudi DHCP klijentu zadani prolaz.
Duljina opcije
4
Dec
1
Vrijednost opcije
172.16.12.1
Retak
4
Broj opcije
6
Dec
1
Opcija koja nudi DHCP klijentu DNS.
Duljina opcije
4
Dec
1
Vrijednost opcije
8.8.8.8
Retak
4
Broj opcije
51
Dec
1
Vrijeme trajanja izdanih mrežnih parametara u sekundama, nakon kojeg DHCP klijent treba ponovno zatražiti.
Duljina opcije
4
Dec
1
Vrijednost opcije
86400
Dec
4
Broj opcije
82
Dec
1
Opcija 82, ponavlja ono što je došlo u DHCPDISCOVER.
Duljina opcije
18
Dec
1
Vrijednost opcije
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Kraj paketa
255
Dec
1
255 označava kraj paketa.
DHCPREQUEST
Nakon što klijent primi DHCPOFFER, oblikuje paket s zahtjevom za mrežnim parametrima ne prema svim DHCP poslužiteljima u mreži, već samo jednom konkretnom, čija mu je ponuda DHCPOFFER bila najprivlačnija. Kriteriji 'privlačnosti' mogu biti različiti i ovise o implementaciji DHCP klijenta. Pr menerima zahtjeva se navodi pomoću MAC adrese DHCP poslužitelja. Također, paket DHCPREQUEST može biti poslan od strane klijenta i bez prethodnog formiranja DHCPDISCOVER, ako je IP adresa već ranije dobivena od poslužitelja.
Tablica strukture paketa DHCPREQUEST
Pozicija u paketu
Naziv vrijednosti (opće prihvaćeno)
Primjer
Prikaz
Bajt
Objašnjenje
1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema poslužitelju, 2 — odgovor od poslužitelja klijentu
2
Vrsta hardvera
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Duljina hardverske adrese
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj međurute
5
Transakcijski ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Klijent ga generira na početku operacije zahtjeva
7
Drugo proteklo vrijeme
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp zastavice
8000
Hex
2
Neki flagovi koji se mogu postaviti kao oznaka parametara protokola. U ovom slučaju je postavljen 'broadcast'
11
IP adresa klijenta
0.0.0.0
Retak
4
IP adresa klijenta (ako postoji)
15
Vaša IP adresa klijenta
172.16.134.61
Retak
4
IP adresa ponuđena od strane poslužitelja (ako postoji)
19
IP adresa sljedećeg poslužitelja
0.0.0.0
Retak
4
IP adresa poslužitelja (ako je poznata)
23
IP adresa agenta preusmjeravanja
172.16.114.41
Retak
4
IP adresa agenta preusmjeravanja (na primjer, switch)
27
MAC adresa klijenta
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Puni sastav MAC adrese klijenta
Hex
10
Rezervirani prostor. Obično popunjen nulama
41
Ime poslužitelja
Retak
64
Ime DHCP poslužitelja. Obično se ne prenosi
105
Naziv boot datoteke
Retak
128
Naziv datoteke na poslužitelju koji se koristi od strane bezdiskovnih stanica prilikom podizanja.
235
Magic cookie
63:82:53:63
Hex
4
«Magični» broj, prema kojem se može odrediti pripada li ovaj paket DHCP protokolu.
DHCP opcije. Mogu se pojaviti u bilo kojem redoslijedu.
236
Broj opcije
53
Dec
3
Opcija 53, koja definira tip paketa DHCP 3 — DHCPREQUEST
Duljina opcije
1
Dec
1
Vrijednost opcije
3
Dec
1
Broj opcije
61
Dec
1
Identifikator klijenta: 01 (za Ethernet) + MAC adresa klijenta
Duljina opcije
7
Dec
1
Vrijednost opcije
01:2c:ab:25:ff:72:a6
Hex
7
Broj opcije
60
Dec
«Vendor class identifier». U mom slučaju javlja verziju DHCP klijenta. Moguće je da druga uređaji vraćaju nešto drugo. Windows, na primjer, izvještava MSFT 5.0
Duljina opcije
11
Dec
Vrijednost opcije
udhcp 0.9.8
Retak
Broj opcije
55
1
Zahtijevani mrežni parametri od strane klijenta. Sadržaj može biti različit.
01 — Maska podmreže
03 — Prolaz
06 — DNS
oc — Ime hosta
0f — ime domene mreže
1c — adresa multicast (broadcast) zahtjeva
42 — ime TFTP poslužitelja
79 — Classless Static Route
Duljina opcije
8
1
Vrijednost opcije
01:03:06:0c:0f:1c:42:79
8
Broj opcije
82
Dec
1
Opcija 82, ponavlja ono što je došlo u DHCPDISCOVER.
Duljina opcije
18
Dec
1
Vrijednost opcije
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Kraj paketa
255
Dec
1
255 označava kraj paketa.
DHCPACK
Kao potvrda da 'da, to je tvoja IP adresa i više je neću dodijeliti nikome drugome' od DHCP poslužitelja služi paket u formatu DHCPACK koji poslužitelj šalje klijentu. Također, kao i ostali paketi, šalje se širokoupno. Iako, u niže prikazanom kodu DHCP poslužitelja implementiranog na Pythonu, za svaki slučaj dupliciram bilo koji širokoupni zahtjev slanjem paketa na određeni IP klijenta, ako je već poznat. Osim toga, DHCP poslužitelju nije važno je li paket DHCPACK stigao do klijenta. Ako klijent ne primi DHCPACK, nakon nekog vremena jednostavno ponavlja DHCPREQUEST.
Tablica strukture paketa DHCPACK
Pozicija u paketu
Naziv vrijednosti (opće prihvaćeno)
Primjer
Prikaz
Bajt
Objašnjenje
1
Boot Request
2
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema poslužitelju, 2 — odgovor od poslužitelja klijentu
2
Vrsta hardvera
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC
3
Duljina hardverske adrese
6
Hex
1
Duljina MAC adrese uređaja
4
Hops
1
Hex
1
Broj međurute
5
Transakcijski ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Klijent ga generira na početku operacije zahtjeva
7
Drugo proteklo vrijeme
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese
9
Bootp zastavice
8000
Hex
2
Neki flagovi koji se mogu postaviti kao oznaka parametara protokola. U ovom slučaju je postavljen 'broadcast'
11
IP adresa klijenta
0.0.0.0
Retak
4
IP adresa klijenta (ako postoji)
15
Vaša IP adresa klijenta
172.16.134.61
Retak
4
IP adresa ponuđena od strane poslužitelja (ako postoji)
19
IP adresa sljedećeg poslužitelja
0.0.0.0
Retak
4
IP adresa poslužitelja (ako je poznata)
23
IP adresa agenta preusmjeravanja
172.16.114.41
Retak
4
IP adresa agenta preusmjeravanja (na primjer, switch)
27
MAC adresa klijenta
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)
31
Puni sastav MAC adrese klijenta
Hex
10
Rezervirani prostor. Obično popunjen nulama
41
Ime poslužitelja
Retak
64
Ime DHCP poslužitelja. Obično se ne prenosi
105
Naziv boot datoteke
Retak
128
Naziv datoteke na poslužitelju koji se koristi od strane bezdiskovnih stanica prilikom podizanja.
235
Magic cookie
63:82:53:63
Hex
4
«Magični» broj, prema kojem se može odrediti pripada li ovaj paket DHCP protokolu.
DHCP opcije. Mogu se pojaviti u bilo kojem redoslijedu.
236
Broj opcije
53
Dec
3
Opcija 53, koja definira tip paketa DHCP 5 — DHCPACK
Duljina opcije
1
Dec
1
Vrijednost opcije
5
Dec
1
Broj opcije
1
Dec
1
Opcija koja nudi DHCP klijentu masku podmreže.
Duljina opcije
4
Dec
1
Vrijednost opcije
255.255.224.0
Retak
4
Broj opcije
3
Dec
1
Opcija koja nudi DHCP klijentu zadani prolaz.
Duljina opcije
4
Dec
1
Vrijednost opcije
172.16.12.1
Retak
4
Broj opcije
6
Dec
1
Opcija koja nudi DHCP klijentu DNS.
Duljina opcije
4
Dec
1
Vrijednost opcije
8.8.8.8
Retak
4
Broj opcije
51
Dec
1
Vrijeme trajanja izdanih mrežnih parametara u sekundama, nakon kojeg DHCP klijent treba ponovno zatražiti.
Duljina opcije
4
Dec
1
Vrijednost opcije
86400
Dec
4
Broj opcije
82
Dec
1
Opcija 82, ponavlja ono što je došlo u DHCPDISCOVER.
Duljina opcije
18
Dec
1
Vrijednost opcije
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Kraj paketa
255
Dec
1
255 označava kraj paketa.
Instalacija
Instalacija se zapravo sastoji u postavljanju potrebnih python modula za rad. Pretpostavlja se da je MySQL već instaliran i konfiguriran.
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
Stvaramo MySQL bazu podataka, učitavamo u nju dump pydhcp.sql, postavljamo datoteku s konfiguracijom.
Konfiguracija
Sve postavke poslužitelja nalaze se u datoteci formata xml. Referentna 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')Sada malo detaljnije o tagovima:
Sekcija dhcpserver opisuje osnovne postavke za pokretanje servera, odnosno:
- host — koji IP adresa sluša server na portu 67
- broadcast — koji IP je broadcast za DHCPOFFER i DHCPACK
- DHCPServer — koji IP ima DHCP server
- LeaseTime vremenski period zakupa izdanog IP adresa
- ThreadLimit — koliko istodobno niti je pokrenuto za obradu pristiglih UDP paketa na portu 67. Smatra se da će pomoći na visoko opterećenim projektima 😉
- defaultMask,defaultRouter,defaultDNS — što se nudi pretplatniku prema zadanim postavkama, ako je IP pronađen u bazi, ali dodatne postavke za njega nisu navedene
Sekcija mysql:
host,username,password,basename — sve govori samo za sebe. Približna struktura baze podataka je objavljena na
Sekcija query: ovdje se opisuju upitnici za dobivanje OFFER/ACK:
- offer_count — broj redaka s upitima koji vraćaju rezultat u obliku ip,mask,router,dns
- offer_n — redak upita. Ako je povratak — prazan, izvršava sljedeći upit offer
- history_sql — upit koji zapisuje, na primjer, u "povijest autorizacije" za pretplatnika
U upitima mogu sudjelovati bilo koje varijable iz sekcije options ili opcije iz DHCP protokola.
Sekcija options. Ovdje postaje zanimljivije. Ovdje možemo kreirati varijable koje možemo koristiti dalje u sekciji query.
Na primjer:
option_82_hex:sw_port1:20:22, ova naredba uzima cijeli string iz DHCP zahtjeva opcije 82, u hex formatu, u rasponu od 20. do 22. bajta uključivo i stavlja ga u novu varijablu sw_port1 (port switcha odakle je došao zahtjev)
option_82_hex:sw_mac:26:40, definira varijablu sw_mac, uzimajući hex iz raspona 26:40
Sve moguće opcije koje se mogu koristiti u zahtjevima mogu se vidjeti pokretanjem servera s ključem -d. Vidjet ćemo okvirno takav log:
--prispjeli paket DHCPINFORM na portu 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'}Stoga, možemo bilo koju varijablu staviti u {} i bit će korištena u SQL zahtjevu.
Zabilježimo za povijest koji je IP adresa klijent dobio:


Pokretanje servera
.\/pydhcpdb.py -d -c config.xml
— d način ispravnog ispisivanja u konzolu DEBUG
— c <ime_datoteke> konfiguracijska datoteka
Analiza
A sada ćemo detaljnije proći kroz implementaciju servera na Pythonu. To je boli. Python se učio "u hodu". Mnogi momenti su napravljeni u stilu: "wow, nekako sam to napravio i radi". Nije uopće optimizirano i ostavljeno je u tom stanju uglavnom zbog malo iskustva u razvoju na Pythonu. Zastat ću na najzanimljivijim momentima implementacije servera u "kodovima".
Parser XML konfiguracijske datoteke
Koristi se standardni Python modul xml.dom. Izgleda jednostavno, ali pri implementaciji nedostajalo je kvalitetne dokumentacije i primjera na mreži koji koriste ovaj 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)Višestruko izvršavanje
Zanimljivo je da je višestruko izvršavanje u Pythonu realizirano vrlo jasno i jednostavno.
def PacketWork(data,addr):
...
# implementacija analize primljenog paketa i odgovora na njega
...
while True:
data, addr = udp_socket.recvfrom(1024) # čekamo UDP paket
thread = threading.Thread(target=PacketWork, args=(data,addr,)).start() # čim stigne - pokrećemo u pozadini prethodno definiranu funkciju PacketWork s parametrima
while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
time.sleep(1) # ako je broj već pokrenutih niti veći od postavki, čekamo dok ih ne bude manjePrijem/otprema DHCP paketa
Da biste presreli UDP pakete koji prolaze kroz mrežnu kartu, potrebno je "podignuti" soket:
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"],67))
, gdje su znakovi:
- AF_INET — označava da će format adrese biti IP: port. Može biti i AF_UNIX — gdje se adresa zadava imenom datoteke.
- SOCK_DGRAM — označava da primamo ne 'sirovi paket', već paket koji je prošao kroz vatrozid i sa djelomično rezanim paketom. Odnosno, dobivamo samo UDP paket bez 'fizičke' omotnice UDP paketa. Ako koristimo oznaku SOCK_RAW, morat ćemo dodatno obraditi i tu 'omotnicu'.
Slanje paketa može biti kao broadcast:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # prebacujemo socket u način slanja broadcasta
rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68)), kao i na adresu 'odakle je paket stigao':
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # prebacujemo socket u način 'više slušatelja'
rz=udp_socket.sendto(packetack, addr), gdje SOL_SOCKET označava 'razinu protokola' za postavljanje opcija,
, SO_BROADCAST je opcija koja označava da paket šaljemo 'broadcastom'
, SO_REUSEADDR je opcija koja prebacuje socket u način 'više slušatelja'. Ideja je da nije potrebna u ovom slučaju, ali na jednom od FreeBSD servera na kojem sam testirao, bez ove opcije kod nije radio.
Razrada DHCP paketa
Ovdje mi se stvarno svidio Python. Naime, iz 'kutije' omogućava prilično slobodno postupanje s bajt kodom. Omogućava vrlo jednostavno pretvaranje u decimalne vrijednosti, nizove i hex — tj. ono što nam je zapravo potrebno da razumijemo strukturu paketa. Tako možemo dobiti raspon bajtova u HEX i običnim bajtovima:
res["xidhex"]=data[4:8].hex()
res["xidbyte"]=data[4:8], pakirati bajtove u strukturu:
res["flags"]=pack('BB',data[10],data[11])Dobiti IP iz strukture:
res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));I obrnuto:
res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])
Na tome je sve 😉
Izvor: habr.com
