
Cílem tohoto projektu bylo:
- Studium protokolu DHCP při práci v síti IPv4
- Studium Pythonu (o něco více než od nuly 😉 )
- náhrada serveru (můj fork), originál , který se pod novým OS stále hůře a hůře sestavuje. A také se mi nelíbí, že binární soubor, který není možné "vyměnit hned teď"
- získání funkčního serveru DHCP s možností výběru IP adresy abonenta podle MAC adresy abonenta nebo vazby MAC switche + port (Option 82)
- napsání dalšího kola (O! to je moje oblíbená činnost)
- dostat na frak za svou nešikovnost na HabrHabr (nebo ještě lépe pozvánku) 😉
Výsledek: funguje 😉 Otestováno na OS FreeBSD a Ubuntu. Teoreticky by kód měl fungovat pod jakýmkoli OS, protože v kódu jako by nebyly žádné specifické vazby.
Pozor! Dále je toho hodně.
Odkaz na repozitář pro nadšence .
Proces instalace, nastavení a používání výsledku "studia matérie" je mnohem níže, a dále je zde trochu teorie o protokolu DHCP. Pro sebe. A pro historii 😉
Trocha teorie
Co je to DHCP
Je to síťový protokol, který umožňuje zařízení zjistit svou IP adresu (a další parametry jako bránu, DNS a další), od serveru DHCP. Výměna paketů probíhá pomocí protokolu UDP. Obecný princip fungování zařízení při žádosti o parametry sítě je následující:
- Zařízení (klient) rozesílá širokospektrální UDP žádost (DHCPDISCOVER) po celé síti s požadavkem "tak kdo mi dá IP adresu?". Obvykle (ale ne vždy) žádost probíhá z portu 68 (zdroje) a cílem je port 67 (cílový). Některá zařízení posílají pakety i z portu 67. Uvnitř paketu DHCPDISCOVER je zahrnut MAC adresu zařízení klienta.
- Všechny servery DHCP, které se nacházejí v síti (a může jich být několik), formují pro zařízení, které poslalo DHCPDISCOVER, nabídku DHCPOFFER s nastavením sítě, a také ji širokospektrálně posílají po síti. Identifikace, komu je tento paket určen, probíhá podle MAC adresy klienta, poskytnuté dříve v žádosti DHCPDISCOVER.
- Klient přijímá balíčky s nabídkami síťových nastavení, vybírá ten nejatraktivnější (kritéria mohou být různá, například i podle doby doručení balíčku, počtu mezilehlých tras) a zasílá v případě vybraného serveru DHCP „oficiální žádost“ DHCPREQUEST se síťovými nastaveními. V tomto případě balíček směřuje již k konkrétnímu serveru DHCP.
- Server, který obdržel DHCPREQUEST, odesílá balíček formátu DHCPACK, ve kterém znovu vypisuje síťová nastavení určená pro tohoto klienta.

Kromě toho existují balíčky DHCPINFORM, které putují od klienta a jejichž cílem je informovat server DHCP, že „klient žije“ a používá vydaná síťová nastavení. V realizaci tohoto serveru jsou tyto balíčky ignorovány.
Formát balíčků
Celkově rámec balíčku Ethernet vypadá přibližně takto:
![]()
V našem případě se zaměříme pouze na data přímo obsahu balíčku UDP, bez hlaviček protokolů úrovní OSI, konkrétně na strukturu DHCP:
DHCPDISCOVER
Tedy proces získávání IP adresy pro zařízení začíná tím, že klient DHCP rozesílá širokopásmovou žádost z portu 68 na 255.255.255.255:67. V tomto balíčku klient zahrnuje svou MAC adresu a také to, co přesně od serveru DHCP chce získat. Struktura balíčku je popsána v tabulce níže.
Tabulka struktury balíčku DHCPDISCOVER
Pozice v balíčku
Název hodnoty
Příklad
Zobrazení
Byt
Vysvětlení
1
Boot Request
1
Hex
1
Typ zprávy. 1 — žádost od klienta k serveru, 2 — odpověď od serveru klientovi.
2
Hardware type
1
Hex
1
Typ hardwarové adresy, v tomto protokolu 1 — MAC.
3
Hardware adrees length
6
Hex
1
Délka MAC adresy zařízení.
4
Hops
1
Hex
1
Počet mezilehlých tras.
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakce. Klient generuje na začátku operace žádosti.
7
Second elapsed
0
Hex
4
Čas v sekundách od začátku procesu získání adresy.
9
Bootp flags
0
Hex
2
Nějaké příznaky, které mohou být nastavovány jako indikace parametrů protokolu.
11
Client IP address
0.0.0.0
Řádek
4
IP adresa klienta (pokud existuje).
15
Your client IP address
0.0.0.0
Řádek
4
IP adresa navržená serverem (pokud existuje).
19
Next server IP address
0.0.0.0
Řádek
4
IP adresa serveru (pokud je známa).
23
Relay agent IP address
172.16.114.41
Řádek
4
IP adresa agenta přeposílání (například switche).
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odesílatele balíčku (klienta).
31
Client hardware address padding
Hex
10
Vyhrazené místo. Obvykle zaplněné nulami.
41
Server host name
Řádek
64
Název serveru DHCP. Obvykle se nepřenáší.
105
Boot file name
Řádek
128
Název souboru na serveru používaný bezdiskovými stanicemi při zavádění
235
Magický cookie
63:82:53:63
Hex
4
„Magické“ číslo, podle kterého lze také určit, že tento paket patří k protokolu DHCP
Možnosti DHCP. Mohou jít v libovolném pořadí
236
Číslo možnosti
53
Dec
1
Možnost 53, která určuje typ paketu DHCP
1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM
Délka možnosti
1
Dec
1
Hodnota možnosti
1
Dec
1
Číslo možnosti
50
Dec
1
Jakou IP adresu chce klient získat
Délka možnosti
4
Dec
1
Hodnota možnosti
172.16.134.61
Řádek
4
Číslo možnosti
55
1
Požadované síťové parametry klientem. Složení může být různé
01 — Maska sítě
03 — Brána
06 — DNS
oc — Název hostitele
0f — název domény sítě
1c — adresa pro široké vysílání (broadcast)
42 — název serveru TFTP
79 — Classless Static Route
Délka možnosti
8
1
Hodnota možnosti
01:03:06:0c:0f:1c:42:79
8
Číslo možnosti
82
Dec
Možnost 82, ve které se předává MAC adresa zařízení — retranslátoru a nějaké další hodnoty.
Nejčastěji — port switche, na kterém funguje koncový klient DHCP. V této možnosti jsou "vložené" dodatečné parametry. První byte — číslo "podmožnosti", druhý její délka, dále její hodnota.
V tomto případě jsou v možnosti 82 vloženy podmožnosti:
Agent Circuit ID = 00:04:00:01:00:04, kde poslední dva byty jsou port klienta DHCP, ze kterého přišel požadavek
Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC adresa zařízení retranslátoru DHCP
Délka možnosti
18
Dec
Hodnota možnosti
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
Konec paketu
255
Dec
1
255 symbolizuje konec paketu
DHCPOFFER
Jakmile server obdrží paket DHCPDISCOVER a pokud vidí, že může klientovi něco nabídnout z požadovaného, formuluje pro něj odpověď — DHCPDISCOVER. Odpověď je odesílána na port "odkud přišla", jako broadcast, protože v tomto okamžiku klient ještě nemá IP adresu, a tedy paket může přijmout pouze pokud je odeslán široce. Klient rozpozná, že se jedná o paket pro něj podle své MAC adresy uvnitř paketu, a také podle čísla transakce, které generuje v okamžiku vytvoření prvního paketu.
Tabulka struktury paketu DHCPOFFER
Pozice v balíčku
Název hodnoty (běžně používaný)
Příklad
Zobrazení
Byt
Vysvětlení
1
Boot Request
1
Hex
1
Typ zprávy. 1 — žádost od klienta k serveru, 2 — odpověď od serveru klientovi.
2
Hardware type
1
Hex
1
Typ hardwarové adresy, v tomto protokolu 1 — MAC.
3
Hardware adrees length
6
Hex
1
Délka MAC adresy zařízení.
4
Hops
1
Hex
1
Počet mezilehlých tras.
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakce. Klient generuje na začátku operace žádosti.
7
Second elapsed
0
Hex
4
Čas v sekundách od začátku procesu získání adresy.
9
Bootp flags
0
Hex
2
Nějaké flagy, které mohou být nastaveny jako označení parametrů protokolu. V tomto případě 0 — znamená typ požadavku Unicast
11
Client IP address
0.0.0.0
Řádek
4
IP adresa klienta (pokud existuje).
15
Your client IP address
172.16.134.61
Řádek
4
IP adresa navržená serverem (pokud existuje).
19
Next server IP address
0.0.0.0
Řádek
4
IP adresa serveru (pokud je známa).
23
Relay agent IP address
172.16.114.41
Řádek
4
IP adresa agenta přeposílání (například switche).
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odesílatele balíčku (klienta).
31
Client hardware address padding
Hex
10
Vyhrazené místo. Obvykle zaplněné nulami.
41
Server host name
Řádek
64
Název serveru DHCP. Obvykle se nepřenáší.
105
Boot file name
Řádek
128
Název souboru na serveru používaný bezdiskovými stanicemi při zavádění
235
Magický cookie
63:82:53:63
Hex
4
„Magické“ číslo, podle kterého lze také určit, že tento paket patří k protokolu DHCP
Možnosti DHCP. Mohou jít v libovolném pořadí
236
Číslo možnosti
53
Dec
1
Možnost 53, která určuje typ paketu DHCP 2 — DHCPOFFER
Délka možnosti
1
Dec
1
Hodnota možnosti
2
Dec
1
Číslo možnosti
1
Dec
1
Možnost nabízející DHCP klientovi masku sítě
Délka možnosti
4
Dec
1
Hodnota možnosti
255.255.224.0
Řádek
4
Číslo možnosti
3
Dec
1
Možnost nabízející DHCP klientovi výchozí bránu
Délka možnosti
4
Dec
1
Hodnota možnosti
172.16.12.1
Řádek
4
Číslo možnosti
6
Dec
1
Možnost nabízející DHCP klientovi DNS
Délka možnosti
4
Dec
1
Hodnota možnosti
8.8.8.8
Řádek
4
Číslo možnosti
51
Dec
1
Délka životnosti vydaných síťových parametrů v sekundách, po které by měl klient DHCP znovu požádat
Délka možnosti
4
Dec
1
Hodnota možnosti
86400
Dec
4
Číslo možnosti
82
Dec
1
Možnost 82 opakuje to, co přišlo v DHCPDISCOVER
Délka možnosti
18
Dec
1
Hodnota možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Konec paketu
255
Dec
1
255 symbolizuje konec paketu
DHCPREQUEST
Po obdržení DHCPOFFER klient vytvoří paket s dotazem na síťové parametry už ne ke všem DHCP serverům v síti, ale pouze k jednomu konkrétnímu, jehož nabídka DHCPOFFER se mu nejvíce „líbil“. Kritéria „líbenosti“ se mohou lišit a závisí na implementaci DHCP klienta. Příjemce dotazu je určen pomocí MAC adresy DHCP serveru. Také může být paket DHCPREQUEST odeslán klientem bez předchozího vytvoření DHCPDISCOVER, pokud již byl dříve získán IP adresu od serveru.
Tabulka struktury paketu DHCPREQUEST
Pozice v balíčku
Název hodnoty (běžně používaný)
Příklad
Zobrazení
Byt
Vysvětlení
1
Boot Request
1
Hex
1
Typ zprávy. 1 — žádost od klienta k serveru, 2 — odpověď od serveru klientovi.
2
Hardware type
1
Hex
1
Typ hardwarové adresy, v tomto protokolu 1 — MAC.
3
Hardware adrees length
6
Hex
1
Délka MAC adresy zařízení.
4
Hops
1
Hex
1
Počet mezilehlých tras.
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakce. Klient generuje na začátku operace žádosti.
7
Second elapsed
0
Hex
4
Čas v sekundách od začátku procesu získání adresy.
9
Bootp flags
8000
Hex
2
Nějaké příznaky, které mohou být nastaveny jako indikace parametrů protokolu. V tomto případě je nastaveno „broadcast“
11
Client IP address
0.0.0.0
Řádek
4
IP adresa klienta (pokud existuje).
15
Your client IP address
172.16.134.61
Řádek
4
IP adresa navržená serverem (pokud existuje).
19
Next server IP address
0.0.0.0
Řádek
4
IP adresa serveru (pokud je známa).
23
Relay agent IP address
172.16.114.41
Řádek
4
IP adresa agenta přeposílání (například switche).
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odesílatele balíčku (klienta).
31
Client hardware address padding
Hex
10
Vyhrazené místo. Obvykle zaplněné nulami.
41
Server host name
Řádek
64
Název serveru DHCP. Obvykle se nepřenáší.
105
Boot file name
Řádek
128
Název souboru na serveru používaný bezdiskovými stanicemi při zavádění
235
Magický cookie
63:82:53:63
Hex
4
„Magické“ číslo, podle kterého lze také určit, že tento paket patří k protokolu DHCP
Možnosti DHCP. Mohou jít v libovolném pořadí
236
Číslo možnosti
53
Dec
3
Možnost 53, určující typ paketu DHCP 3 — DHCPREQUEST
Délka možnosti
1
Dec
1
Hodnota možnosti
3
Dec
1
Číslo možnosti
61
Dec
1
Identifikátor klienta: 01 (pro Ethernet) + MAC adresa klienta
Délka možnosti
7
Dec
1
Hodnota možnosti
01:2c:ab:25:ff:72:a6
Hex
7
Číslo možnosti
60
Dec
„Vendor class identifier“. V mém případě uvádí verzi DHCP klienta. Možná jiná zařízení vrací něco jiného. Windows například hlásí MSFT 5.0
Délka možnosti
11
Dec
Hodnota možnosti
udhcp 0.9.8
Řádek
Číslo možnosti
55
1
Požadované síťové parametry klientem. Složení může být různé
01 — Maska sítě
03 — Brána
06 — DNS
oc — Název hostitele
0f — název domény sítě
1c — adresa pro široké vysílání (broadcast)
42 — název serveru TFTP
79 — Classless Static Route
Délka možnosti
8
1
Hodnota možnosti
01:03:06:0c:0f:1c:42:79
8
Číslo možnosti
82
Dec
1
Možnost 82 opakuje to, co přišlo v DHCPDISCOVER
Délka možnosti
18
Dec
1
Hodnota možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Konec paketu
255
Dec
1
255 symbolizuje konec paketu
DHCPACK
Jako potvrzení toho, že „ano, to je tvoje IP adresa a nikomu jinému ji nedám“ od DHCP serveru slouží paket ve formátu DHCPACK od serveru klientovi. Také, stejně jako ostatní pakety, je odesílán širokopásmově. Ačkoli, v níže uvedeném kódu DHCP serveru implementovaného v Pythonu, z bezpečnostních důvodů duplikuji jakýkoli širokopásmový dotaz zasláním paketu na konkrétní IP klienta, pokud je již známá. Přičemž DHCP server se vůbec nezajímá o to, zda paket DHCPACK dorazil k klientovi. Pokud klient nedostane DHCPACK, po nějaké době prostě zopakuje DHCPREQUEST.
Tabulka struktury paketu DHCPACK
Pozice v balíčku
Název hodnoty (běžně používaný)
Příklad
Zobrazení
Byt
Vysvětlení
1
Boot Request
2
Hex
1
Typ zprávy. 1 — žádost od klienta k serveru, 2 — odpověď od serveru klientovi.
2
Hardware type
1
Hex
1
Typ hardwarové adresy, v tomto protokolu 1 — MAC.
3
Hardware adrees length
6
Hex
1
Délka MAC adresy zařízení.
4
Hops
1
Hex
1
Počet mezilehlých tras.
5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakce. Klient generuje na začátku operace žádosti.
7
Second elapsed
0
Hex
4
Čas v sekundách od začátku procesu získání adresy.
9
Bootp flags
8000
Hex
2
Nějaké příznaky, které mohou být nastaveny jako indikace parametrů protokolu. V tomto případě je nastaveno „broadcast“
11
Client IP address
0.0.0.0
Řádek
4
IP adresa klienta (pokud existuje).
15
Your client IP address
172.16.134.61
Řádek
4
IP adresa navržená serverem (pokud existuje).
19
Next server IP address
0.0.0.0
Řádek
4
IP adresa serveru (pokud je známa).
23
Relay agent IP address
172.16.114.41
Řádek
4
IP adresa agenta přeposílání (například switche).
27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odesílatele balíčku (klienta).
31
Client hardware address padding
Hex
10
Vyhrazené místo. Obvykle zaplněné nulami.
41
Server host name
Řádek
64
Název serveru DHCP. Obvykle se nepřenáší.
105
Boot file name
Řádek
128
Název souboru na serveru používaný bezdiskovými stanicemi při zavádění
235
Magický cookie
63:82:53:63
Hex
4
„Magické“ číslo, podle kterého lze také určit, že tento paket patří k protokolu DHCP
Možnosti DHCP. Mohou jít v libovolném pořadí
236
Číslo možnosti
53
Dec
3
Možnost 53, určující typ paketu DHCP 5 — DHCPACK
Délka možnosti
1
Dec
1
Hodnota možnosti
5
Dec
1
Číslo možnosti
1
Dec
1
Možnost nabízející DHCP klientovi masku sítě
Délka možnosti
4
Dec
1
Hodnota možnosti
255.255.224.0
Řádek
4
Číslo možnosti
3
Dec
1
Možnost nabízející DHCP klientovi výchozí bránu
Délka možnosti
4
Dec
1
Hodnota možnosti
172.16.12.1
Řádek
4
Číslo možnosti
6
Dec
1
Možnost nabízející DHCP klientovi DNS
Délka možnosti
4
Dec
1
Hodnota možnosti
8.8.8.8
Řádek
4
Číslo možnosti
51
Dec
1
Délka životnosti vydaných síťových parametrů v sekundách, po které by měl klient DHCP znovu požádat
Délka možnosti
4
Dec
1
Hodnota možnosti
86400
Dec
4
Číslo možnosti
82
Dec
1
Možnost 82 opakuje to, co přišlo v DHCPDISCOVER
Délka možnosti
18
Dec
1
Hodnota možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18
Konec paketu
255
Dec
1
255 symbolizuje konec paketu
Instalace
Instalace fakticky zahrnuje instalaci modulů python potřebných pro provoz. Předpokládá se, že MySQL již je nainstalována a nastavena.
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
Vytvořte databázi MySQL, nahrajte do ní dump pydhcp.sql, nastavte konfigurační soubor.
Konfigurace
Všechna nastavení serveru se nacházejí v souboru ve formátu XML. Referenční soubor:
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')Nyní podrobněji k tagům:
Sekce dhcpserver popisuje základní nastavení pro spuštění serveru, konkrétně:
- host — na jaké IP adrese server naslouchá na portu 67
- broadcast — která IP adresa je broadcastem pro DHCPOFFER a DHCPACK
- DHCPServer — jaká je IP adresa DHCP serveru
- LeaseTime je doba pronájmu přidělené IP adresy
- ThreadLimit — kolik současně vláken je spuštěno pro zpracování přijatých UDP paketů na portu 67. Předpokládá se, že to pomůže u vysoce zatížených projektů 😉
- defaultMask, defaultRouter, defaultDNS — to, co se nabídne uživateli jako výchozí, pokud je IP v databázi nalezena, ale další parametry pro něj nejsou uvedeny
Sekce mysql:
host, username, password, basename — vše mluví samo za sebe. Přibližná struktura databáze je uvedena na
Sekce query: zde jsou popsány dotazy pro získání OFFER/ACK:
- offer_count — počet řádků s dotazy, které vracejí výsledek ve formátu ip, mask, router, dns
- offer_n — řádek dotazu. Pokud se návrat — je prázdný, provede se následující dotaz offer
- history_sql — dotaz zapisující například do "historie autorizace" pro uživatele
V dotazech mohou být použity jakékoli proměnné ze sekce options nebo volby z protokolu DHCP.
Sekce options. Tady už je to zajímavější. Můžeme zde vytvářet proměnné, které můžeme později použít v sekci query.
Například:
option_82_hex:sw_port1:20:22, tento příkaz vezme celou řetězec přijatý v DHCP požadavku s možností 82, v hexadecimálním formátu, v rozmezí od 20 do 22 byte včetně a uloží jej do nové proměnné sw_port1 (port switche, ze kterého přišel požadavek)
option_82_hex:sw_mac:26:40, určujeme proměnnou sw_mac, vezmeme hex z rozsahu 26:40
Všechny možné možnosti, které lze použít v požadavcích, můžeme vidět spuštěním serveru s klíčem -d. Uvidíme přibližně takový log:
--přijato DHCPINFORM paket na port 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'}Odpovídajícím způsobem můžeme jakoukoli proměnnou obalit do {} a ta bude použita v SQL dotazu.
Zaznamenejme pro budoucnost, že IP adresu klient získal:


Spuštění serveru
.\/pydhcpdb.py -d -c config.xml
— d režim výstupu do konzole DEBUG
— c <jméno_souboru> konfigurační soubor
Analýza výkonu
A nyní podrobněji k implementaci serveru v Pythonu. To je bolest. Python byl studován 'za běhu'. Mnoho bodů je uděláno stylem: 'wow, nějak jsem to udělal a funguje to'. Úplně neoptimalizováno a ponecháno v takovém stavu převážně kvůli malé zkušenosti s vývojem v Pythonu. Zaměřím se na nejzajímavější momenty implementace serveru v 'kódu'.
Parser konfiguračního souboru XML
Používá se standardní modul Python xml.dom. Zdá se to snadné, ale při implementaci výrazně chyběla kvalitní dokumentace a příklady na internetu s použitím tohoto modulu.
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)Vícevláknovost
O podivuhodné, vícerozměrnost v Pythonu je realizována velmi jasně a jednoduše.
def PacketWork(data, addr):
...
# implementace zpracování přijatého paketu a jeho odpovědi
...
while True:
data, addr = udp_socket.recvfrom(1024) # čekáme na UDP paket
thread = threading.Thread(target=PacketWork, args=(data, addr,)).start() # jakmile přijde, spustíme na pozadí dříve definovanou funkci PacketWork s parametry
while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
time.sleep(1) # pokud je počet již spuštěných vláken větší než v nastavení, čekáme, až jich bude méněPřijímání/odesílání paketu DHCP
Abychom mohli zachytit UDP pakety procházející síťovou kartou, musíme "aktivovat" soket:
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"], 67))
, kde flagy:
- AF_INET — znamená, že formát adresy bude IP: port. Může být také AF_UNIX — kde adresa je určena názvem souboru.
- SOCK_DGRAM — znamená, že přijímáme ne „syrový paket“, ale již prošlý firewallem, a s částečně ořezaným paketem. Tedy získáváme pouze paket UDP bez „fyzické“ části obálky paketu UDP. Pokud použijete flag SOCK_RAW, bude nutné také parsovat a tuto „obálku“.
Odeslání paketu může být jako broadcast:
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # přepínáme socket do režimu odesílání broadcastu
rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68)), tak i na adresu, „odkud paket přišel“:
udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # přepínáme socket do režimu "mnoho posluchačů"
rz=udp_socket.sendto(packetack, addr), kde SOL_SOCKET znamená „úroveň protokolu“ pro nastavení možností,
, SO_BROADCAST je možnost, že paket posíláme „broadcastem“
, SO_REUSEADDR je možnost přepínající socket do režimu „mnoho posluchačů“. V zásadě není v tomto případě nutná, ale na jednom serveru FreeBSD, na kterém jsem testoval, bez této možnosti kód nefungoval.
Analýza paketu DHCP
Tady mě opravdu potěšil Python. Ukazuje se, že „z krabice“ umožňuje docela svobodně manipulovat s bajtovým kódem. Umožňuje ho velmi snadno převádět na desetinné hodnoty, řetězce a hex – tj. to, co vlastně potřebujeme k pochopení struktury paketu. Například lze získat rozsah bajtů v HEX a jednoduše bytech:
res["xidhex"]=data[4:8].hex()
res["xidbyte"]=data[4:8], zabalit bajty do struktury:
res["flags"]=pack('BB',data[10],data[11])Získat IP z struktury:
res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));A naopak:
res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])
Na tomhle končíme 😉
Zdroj: habr.com
