DHCP+Mysql server na Python

DHCP+Mysql server na Python

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 DB2DHCP (můj fork), originál zde, 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 "sáhnout si na to živě".

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í:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

DHCP+Mysql server na Python

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:

DHCP+Mysql server na Python

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 GitHub

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:

DHCP+Mysql server na Python

DHCP+Mysql server na Python

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

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster