DHCP+Mysql server na Pythone

DHCP+Mysql server na Pythone

Cieľom tohto projektu bolo:

  • Skúmanie protokolu DHCP pri práci v sieti IPv4
  • Naučiť sa Python (trochu viac ako od nuly 😉 )
  • výmena serveru DB2DHCP (môj fork), originál tu, ktorý sa pod novou OS stále ťažšie a ťažšie stavuje. A ani sa mi nepáči, že binárka, ktorú nemožno „zmeniť hneď“,
  • získanie funkčného DHCP servera s možnosťou výberu IP adresy účastníka podľa MAC adresy účastníka alebo kombinácie MAC switche + port (Option 82)
  • napísanie ďalšieho bicykla (O! to je moja obľúbená činnosť)
  • získanie kritiky za svoju nedokonalosť na Habrahabr (a lepšie pozvanie) 😉

Výsledok: funguje 😉 Vyskúšané na OS FreeBSD a Ubuntu. Teoreticky by sa kód dal požiadať o prácu na akejkoľvek OS, pretože špecifické väzby v kóde sa zdajú byť neprítomné.
Pozor! Ďalej je toho veľa.

Odkaz na repozitár pre milovníkov „dotknúť sa naživo“.

Proces inštalácie, nastavenia a používania výsledku „štúdia materiálu“ je ďalej, a potom trochu teórie o protokole DHCP. Pre seba. A pre históriu 😉

Trochu teórie

Čo je DHCP

Je to sieťový protokol, ktorý umožňuje zariadeniu získať svoju IP adresu (a iné parametre ako bránu, DNS a podobne) od servera DHCP. Výmena paketov prebieha cez protokol UDP. Celkový princíp fungovania zariadenia pri žiadosti o sieťové parametre je nasledujúci:

  1. Zariadenie (klient) vysiela širokopásmovú UDP požiadavku (DHCPDISCOVER) po celej sieti s požiadavkou "nuž, niekto, dajte mi IP adresu". Zvyčajne (ale nie vždy) sa požiadavka odošle z portu 68 (zdroj) a určenie je na port 67 (cieľ). Niektoré zariadenia posielajú pakety aj z portu 67. Vnútri paketu DHCPDISCOVER je zahrnutá MAC adresa zariadenia klienta.
  2. Všetky DHCP servery nachádzajúce sa v sieti (a môže ich byť niekoľko) vytvárajú pre zariadenie, ktoré poslalo DHCPDISCOVER, ponuku DHCPOFFER so sieťovými nastaveniami a tiež ju širšie odosielajú v sieti. Identifikácia, komu je tento paket určený, prebieha cez MAC adresu klienta poskytnutú v požiadavke DHCPDISCOVER.
  3. Klient prijíma balíky s ponukami sieťových nastavení, vyberá si najatraktívnejší (kritériá môžu byť rôzne, napríklad aj podľa času dodania balíka, počtu medzimestských trás) a na vybraný server DHCP posiela "oficiálny dopyt" DHCPREQUEST so sieťovými nastaveniami. V tomto prípade balík smeruje už k konkrétnemu serveru DHCP.
  4. Server, ktorý dostane DHCPREQUEST, odosiela balík formátu DHCPACK, v ktorom opäť uvádza sieťové nastavenia určené pre daného klienta.

DHCP+Mysql server na Pythone

Okrem toho existujú balíky DHCPINFORM, ktoré idú od klienta a cieľom je informovať DHCP server o tom, že "klient žije" a využíva vydané sieťové nastavenia. V implementácii tohto servera sa tieto balíky ignorujú.

Formát balíkov

Vo všeobecnosti rámec balíka Ethernet vyzerá zhruba takto:

DHCP+Mysql server na Pythone

V našom prípade sa zameriame len na dáta priamo v obsahu balíka UDP, bez hlavičiek protokolov úrovní OSI, a to konkrétne na štruktúru DHCP:

DHCPDISCOVER

Takže proces získavania IP adresy pre zariadenie začína tým, že klient DHCP rozosiela vysielací dopyt z portu 68 na 255.255.255.255:67. V tomto balíku klient uvádza svoju MAC adresu, ako aj to, čo konkrétne chce od DHCP servera. Štruktúra balíka je popísaná v tabuľke nižšie.

Tabuľka štruktúry balíka DHCPDISCOVER

Pozícia v balíku
Názov hodnoty
Príklad
Pohľad
Bajt
Vysvetlenie

1
Boot Request
1
Hex
1
Typ správy. 1 — dopyt od klienta k serveru, 2 — odpoveď od servera klientovi

2
Hardware type
1
Hex
1
Typ hardvérové adresy, v tomto protokole 1 — MAC

3
Hardware address length
6
Hex
1
Dĺžka MAC adresy zariadenia

4
Hops
1
Hex
1
Počet medzimestských trás

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakcie. Generuje ho klient na začiatku operácie dopytu.

7
Second elapsed
0
Hex
4
Čas v sekundách od začiatku procesu získania adresy

9
Bootp flags
0
Hex
2
Určité vlajky, ktoré môžu byť nastavené ako indikácia parametrov protokolu.

11
Client IP address
0.0.0.0
Riadok
4
IP adresa klienta (ak existuje)

15
Your client IP address
0.0.0.0
Riadok
4
IP adresa navrhnutá serverom (ak existuje)

19
Next server IP address
0.0.0.0
Riadok
4
IP adresa servera (ak je známa)

23
Relay agent IP address
172.16.114.41
Riadok
4
IP adresa agenta retranslácie (napríklad prepínača)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odosielateľa balíka (klienta)

31
Client hardware address padding
 
Hex
10
Rezervované miesto. Zvyčajne zaplnené nulami.

41
Server host name
 
Riadok
64
Názov servera DHCP. Zvyčajne sa neprenáša.

105
Boot file name
 
Riadok
128
Názov súboru na serveri používaný bezdiskovými stanicami pri spúšťaní

235
Magic cookie
63:82:53:63
Hex
4
„Magické“ číslo, podľa ktorého sa dá určiť, že tento paket patrí protokolu DHCP

Možnosti DHCP. Môžu byť v akomkoľvek poradí

236
Číslo možnosti
53
Dec
1
Možnosť 53, ktorá určuje typ DHCP paketu

1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM

 
Dĺžka možnosti
1
Dec
1

 
Hodnota možnosti
1
Dec
1

 
Číslo možnosti
50
Dec
1
Akú IP adresu chce získať klient

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
172.16.134.61
Riadok
4

 
Číslo možnosti
55
 
1
Jednotlivé sieťové parametre, ktoré si klient vyžaduje. Zloženie môže byť rôzne

01 — Maska siete
03 — Brána
06 — DNS
oc — Názov hostiteľa
0f — Názov domény siete
1c — Adresa brány (broadcast)
42 — Názov TFTP servera
79 — Classless Static Route

 
Dĺžka možnosti
8
 
1

 
Hodnota možnosti
01:03:06:0c:0f:1c:42:79
 
8

 
Číslo možnosti
82
Dec
 
Možnosť 82, v ktorej je prenášaná MAC adresa zariadenia — retranslátora a nejaké ďalšie hodnoty.

Často — port switche, na ktorom pracuje koncový DHCP klient. V tejto možnosti sú „vložené“ dodatočné parametre. Prvý byte — číslo „submožnosti“, druhý jej dĺžka, ďalej jej hodnota.

V tomto prípade sú v možnosti 82 vložené submožnosti:
Agent Circuit ID = 00:04:00:01:00:04, kde posledné dva byty sú port klienta DHCP, z ktorého prišla požiadavka

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC adresa retranslátora DHCP

 
Dĺžka 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
 

 
Koniec paketu
255
Dec
1
255 symbolizuje koniec paketu

DHCPOFFER

Akonáhle server príjme paket DHCPDISCOVER a ak vidí, že môže klientovi ponúknuť niečo z požadovaného, vytvorí odpoveď — DHCPOFFER. Odpoveď sa posiela na port „odkiaľ prišla“, ako broadcast, pretože v tomto momente klient ešte nemá IP adresu, a teda paket môže prijať len ak bol odoslaný širokovysielaním. Klient rozpozná, že ide o paket preňho, podľa svojej MAC adresy v pakete, ako aj čísla transakcie, ktoré generuje v okamihu vytvorenia prvého paketu.

Tabuľka štruktúry paketu DHCPOFFER

Pozícia v balíku
Názov hodnoty (obe dôležité)
Príklad
Pohľad
Bajt
Vysvetlenie

1
Boot Request
1
Hex
1
Typ správy. 1 — dopyt od klienta k serveru, 2 — odpoveď od servera klientovi

2
Hardware type
1
Hex
1
Typ hardvérové adresy, v tomto protokole 1 — MAC

3
Hardware address length
6
Hex
1
Dĺžka MAC adresy zariadenia

4
Hops
1
Hex
1
Počet medzimestských trás

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakcie. Generuje ho klient na začiatku operácie dopytu.

7
Second elapsed
0
Hex
4
Čas v sekundách od začiatku procesu získania adresy

9
Bootp flags
0
Hex
2
Niektoré vlajky, ktoré môžu byť nastavené ako indikátory parametrov protokolu. V tomto prípade, 0 — znamená typ požiadavky Unicast

11
Client IP address
0.0.0.0
Riadok
4
IP adresa klienta (ak existuje)

15
Your client IP address
172.16.134.61
Riadok
4
IP adresa navrhnutá serverom (ak existuje)

19
Next server IP address
0.0.0.0
Riadok
4
IP adresa servera (ak je známa)

23
Relay agent IP address
172.16.114.41
Riadok
4
IP adresa agenta retranslácie (napríklad prepínača)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odosielateľa balíka (klienta)

31
Client hardware address padding
 
Hex
10
Rezervované miesto. Zvyčajne zaplnené nulami.

41
Server host name
 
Riadok
64
Názov servera DHCP. Zvyčajne sa neprenáša.

105
Boot file name
 
Riadok
128
Názov súboru na serveri používaný bezdiskovými stanicami pri spúšťaní

235
Magic cookie
63:82:53:63
Hex
4
„Magické“ číslo, podľa ktorého sa dá určiť, že tento paket patrí protokolu DHCP

Možnosti DHCP. Môžu byť v akomkoľvek poradí

236
Číslo možnosti
53
Dec
1
Možnosť 53, ktorá určuje typ paketu DHCP 2 — DHCPOFFER

 
Dĺžka možnosti
1
Dec
1

 
Hodnota možnosti
2
Dec
1

 
Číslo možnosti
1
Dec
1
Možnosť ponúkajúca DHCP klientovi masku siete

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
255.255.224.0
Riadok
4

 
Číslo možnosti
3
Dec
1
Možnosť ponúkajúca DHCP klientovi predvolenú bránu

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
172.16.12.1
Riadok
4

 
Číslo možnosti
6
Dec
1
Možnosť ponúkajúca DHCP klientovi DNS

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
8.8.8.8
Riadok
4

 
Číslo možnosti
51
Dec
1
Časová životnosť pridelených sieťových parametrov v sekundách, po ktorých by si mal DHCP klient vyžiadať ich znovu

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
86400
Dec
4

 
Číslo možnosti
82
Dec
1
Možnosť 82, ktorá opakuje to, čo prišlo v DHCPDISCOVER

 
Dĺžka 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

 
Koniec paketu
255
Dec
1
255 symbolizuje koniec paketu

DHCPREQUEST

Keď klient dostane DHCPOFFER, vytvorí paket so žiadosťou o sieťové parametre nie ku všetkým DHCP serverom v sieti, ale len k jednému konkrétnemu, ktorého ponuka DHCPOFFER mu najviac vyhovovala. Kritériá, podľa ktorých sa mu ponuka „páčila“, sa môžu líšiť a závisia od implementácie DHCP klienta. Prijímateľ žiadosti sa uvádza pomocou MAC adresy DHCP servera. Paket DHCPREQUEST môže byť tiež zaslaný klientom bez predchádzajúcej zostavy DHCPDISCOVER, ak už bola niekedy predtým získaná IP adresa zo servera.

Tabuľka štruktúry paketu DHCPREQUEST

Pozícia v balíku
Názov hodnoty (obe dôležité)
Príklad
Pohľad
Bajt
Vysvetlenie

1
Boot Request
1
Hex
1
Typ správy. 1 — dopyt od klienta k serveru, 2 — odpoveď od servera klientovi

2
Hardware type
1
Hex
1
Typ hardvérové adresy, v tomto protokole 1 — MAC

3
Hardware address length
6
Hex
1
Dĺžka MAC adresy zariadenia

4
Hops
1
Hex
1
Počet medzimestských trás

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakcie. Generuje ho klient na začiatku operácie dopytu.

7
Second elapsed
0
Hex
4
Čas v sekundách od začiatku procesu získania adresy

9
Bootp flags
8000
Hex
2
Určité flagy, ktoré môžu byť nastavené ako indikácia parametrov protokolu. V tomto prípade sú nastavené na "broadcast".

11
Client IP address
0.0.0.0
Riadok
4
IP adresa klienta (ak existuje)

15
Your client IP address
172.16.134.61
Riadok
4
IP adresa navrhnutá serverom (ak existuje)

19
Next server IP address
0.0.0.0
Riadok
4
IP adresa servera (ak je známa)

23
Relay agent IP address
172.16.114.41
Riadok
4
IP adresa agenta retranslácie (napríklad prepínača)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odosielateľa balíka (klienta)

31
Client hardware address padding
 
Hex
10
Rezervované miesto. Zvyčajne zaplnené nulami.

41
Server host name
 
Riadok
64
Názov servera DHCP. Zvyčajne sa neprenáša.

105
Boot file name
 
Riadok
128
Názov súboru na serveri používaný bezdiskovými stanicami pri spúšťaní

235
Magic cookie
63:82:53:63
Hex
4
„Magické“ číslo, podľa ktorého sa dá určiť, že tento paket patrí protokolu DHCP

Možnosti DHCP. Môžu byť v akomkoľvek poradí

236
Číslo možnosti
53
Dec
3
Možnosť 53, ktorá určuje typ paketu DHCP 3 — DHCPREQUEST

 
Dĺžka možnosti
1
Dec
1

 
Hodnota možnosti
3
Dec
1

 
Číslo možnosti
61
Dec
1
Identifikátor klienta: 01 (pre Ethernet) + MAC adresa klienta

 
Dĺžka 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 mojom prípade hlási verziu DHCP klienta. Je možné, že iné zariadenia vracajú niečo iné. Windows napríklad hlási MSFT 5.0.

 
Dĺžka možnosti
11
Dec
 

 
Hodnota možnosti
udhcp 0.9.8
Riadok
 

 
Číslo možnosti
55
 
1
Jednotlivé sieťové parametre, ktoré si klient vyžaduje. Zloženie môže byť rôzne

01 — Maska siete
03 — Brána
06 — DNS
oc — Názov hostiteľa
0f — Názov domény siete
1c — Adresa brány (broadcast)
42 — Názov TFTP servera
79 — Classless Static Route

 
Dĺžka možnosti
8
 
1

 
Hodnota možnosti
01:03:06:0c:0f:1c:42:79
 
8

 
Číslo možnosti
82
Dec
1
Možnosť 82, ktorá opakuje to, čo prišlo v DHCPDISCOVER

 
Dĺžka 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

 
Koniec paketu
255
Dec
1
255 symbolizuje koniec paketu

DHCPACK

Ako potvrdenie, že „áno, to je tvoja IP adresa a nikomu inému ju nedám“, od DHCP servera slúži paket vo formáte DHCPACK, ktorý server posiela klientovi. Tak ako ostatné pakety, aj tento je zaslaný širokopásmovo. Hoci v nižšie uvedenom kóde DHCP servera implementovaného na Pythone, pre istotu duplicujem akýkoľvek širokopásmový dopyt, posielaním paketu na konkrétnu IP adresu klienta, ak je už známa. DHCP server sa vôbec nezaujíma o to, či paket DHCPACK dorazil ku klientovi. Ak klient neobdrží DHCPACK, po chvíli jednoducho zopakuje DHCPREQUEST.

Tabuľka štruktúry paketu DHCPACK

Pozícia v balíku
Názov hodnoty (obe dôležité)
Príklad
Pohľad
Bajt
Vysvetlenie

1
Boot Request
2
Hex
1
Typ správy. 1 — dopyt od klienta k serveru, 2 — odpoveď od servera klientovi

2
Hardware type
1
Hex
1
Typ hardvérové adresy, v tomto protokole 1 — MAC

3
Hardware address length
6
Hex
1
Dĺžka MAC adresy zariadenia

4
Hops
1
Hex
1
Počet medzimestských trás

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinečný identifikátor transakcie. Generuje ho klient na začiatku operácie dopytu.

7
Second elapsed
0
Hex
4
Čas v sekundách od začiatku procesu získania adresy

9
Bootp flags
8000
Hex
2
Určité flagy, ktoré môžu byť nastavené ako indikácia parametrov protokolu. V tomto prípade sú nastavené na "broadcast".

11
Client IP address
0.0.0.0
Riadok
4
IP adresa klienta (ak existuje)

15
Your client IP address
172.16.134.61
Riadok
4
IP adresa navrhnutá serverom (ak existuje)

19
Next server IP address
0.0.0.0
Riadok
4
IP adresa servera (ak je známa)

23
Relay agent IP address
172.16.114.41
Riadok
4
IP adresa agenta retranslácie (napríklad prepínača)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa odosielateľa balíka (klienta)

31
Client hardware address padding
 
Hex
10
Rezervované miesto. Zvyčajne zaplnené nulami.

41
Server host name
 
Riadok
64
Názov servera DHCP. Zvyčajne sa neprenáša.

105
Boot file name
 
Riadok
128
Názov súboru na serveri používaný bezdiskovými stanicami pri spúšťaní

235
Magic cookie
63:82:53:63
Hex
4
„Magické“ číslo, podľa ktorého sa dá určiť, že tento paket patrí protokolu DHCP

Možnosti DHCP. Môžu byť v akomkoľvek poradí

236
Číslo možnosti
53
Dec
3
Možnosť 53, ktorá určuje typ paketu DHCP 5 — DHCPACK

 
Dĺžka možnosti
1
Dec
1

 
Hodnota možnosti
5
Dec
1

 
Číslo možnosti
1
Dec
1
Možnosť ponúkajúca DHCP klientovi masku siete

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
255.255.224.0
Riadok
4

 
Číslo možnosti
3
Dec
1
Možnosť ponúkajúca DHCP klientovi predvolenú bránu

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
172.16.12.1
Riadok
4

 
Číslo možnosti
6
Dec
1
Možnosť ponúkajúca DHCP klientovi DNS

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
8.8.8.8
Riadok
4

 
Číslo možnosti
51
Dec
1
Časová životnosť pridelených sieťových parametrov v sekundách, po ktorých by si mal DHCP klient vyžiadať ich znovu

 
Dĺžka možnosti
4
Dec
1

 
Hodnota možnosti
86400
Dec
4

 
Číslo možnosti
82
Dec
1
Možnosť 82, ktorá opakuje to, čo prišlo v DHCPDISCOVER

 
Dĺžka 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

 
Koniec paketu
255
Dec
1
255 symbolizuje koniec paketu

Inštalácia

Inštalácia vlastne spočíva v inštalácii potrebných Python modulov pre fungovanie. Predpokladá sa, že MySQL je už nainštalovaný a nakonfigurovaný.

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

Vytvárame MySQL databázu, nahráme do nej dump pydhcp.sql a nastavíme konfiguračný súbor.

Konfigurácia

Všetky nastavenia servera sú uložené v súbore formátu xml. Referenčný súbor:

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')

Teraz podrobnejšie k tagom:

Sekcia dhcpserver popisuje hlavné nastavenia pre spustenie servera, a to najmä:

  • host — akú IP adresu server počúva na porte 67
  • broadcast — aká IP je broadcastom pre DHCPOFFER a DHCPACK
  • DHCPServer — aká IP patrí DHCP serveru
  • LeaseTime je čas prenájmu pridelené IP adresy
  • ThreadLimit — koľko naraz spustených vlákien spracováva prichádzajúce UDP pakety na porte 67. Očakáva sa, že to pomôže pri vysoko zaťažených projektoch 😉
  • defaultMask, defaultRouter, defaultDNS — to, čo sa ponúka abonentovi predvolene, ak je IP nájdená v databáze, ale ďalšie parametre pre ňu nie sú uvedené

Sekcia mysql:

host, username, password, basename — všetko hovorí samo za seba. Príkladná štruktúra databázy je vyložená na GitHub

Sekcia query: tu sú popísané dotazy na získanie OFFER/ACK:

  • offer_count — počet riadkov s dotazmi, ktoré vracajú výsledok vo forme ip, mask, router, dns
  • offer_n — riadok dotazu. Ak je návrat prázdny, vykonáva nasledujúci dotaz offer
  • history_sql — dotaz zapisujúci napríklad do "historie autorizácie" podľa abonenta

Vo vyhľadávaniach môžu účinkovať akékoľvek premenné zo sekcie options alebo možnosti z protokolu DHCP.

Sekcia options. Tu už to je zaujímavejšie. Tu môžeme vytvárať premenné, ktoré môžeme neskôr používať v sekcii query.

Napríklad:

option_82_hex:sw_port1:20:22

, tento príkaz zoberie celý reťazec prichádzajúci v DHCP požiadavke s možnosťou 82, v hexa formáte, v rozmedzí od 20 do 22 bajtov vrátane, a uloží ho do novej premennej sw_port1 (port switche, z ktorého prišla požiadavka)

option_82_hex:sw_mac:26:40

, definujeme premennú sw_mac, vezmúc hex z rozsahu 26:40

Zobraziť všetky možné možnosti, ktoré možno použiť v požiadavkách, môžeme spustením servera s kľúčom -d. Uvidíme zhruba takýto log:

--prišiel paket DHCPINFORM 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'}

Podľa toho môžeme akúkoľvek premennú obaliť do {} a bude použitá v SQL požiadavke.

Zaznamenajme pre históriu, akú IP adresu klient získal:

DHCP+Mysql server na Pythone

DHCP+Mysql server na Pythone

Spustenie servera

.\/pydhcpdb.py -d -c config.xml

— d režim výstupu do konzoly DEBUG
— c <názov_súboru> konfiguračný súbor

Analýza prevádzky

A teraz podrobnejšie o implementácii servera v Pythone. To je bolestivá záležitosť. Python bol študovaný "za pochodu". Mnohé aspekty sú vykonané v štýle: "wow, nejako som to urobil, tak to funguje". Nie sú vôbec optimalizované a zostať v takom stave hlavne kvôli malým skúsenostiam s vývojom v Pythone. Zameriam sa na najzaujímavejšie momenty implementácie servera v "kóde".

Parser súboru konfigurácie XML

Používa sa štandardný modul Python xml.dom. Zdá sa, že je to jednoduché, ale pri realizácii chýbala poriadna dokumentácia a príklady v sieti na použitie tohto 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)

Viacvláknovosť

Prekvapivo, multithreading v Pythone je implementované veľmi jasne a jednoducho.

def PacketWork(data, addr): 
...
# implementácia analýzy prijatého paketu a odpovede na ňu
...


while True:
    data, addr = udp_socket.recvfrom(1024) # čakáme na UDP paket
    thread = threading.Thread(target=PacketWork, args=(data, addr,)).start()	# akonáhle príde, spustíme na pozadí predtým definovanú funkciu PacketWork s parametrami
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # ak už počet spustených vlákien prekročil nastavenia, čakáme, kým ich bude menej

Prijímanie/odosielanie DHCP paketu

Aby ste mohli zachytiť UDP pakety prechádzajúce sieťovou kartou, musíte "zdvihnúť" socket:

udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
udp_socket.bind((gconfig["dhcp_host"], 67))

, kde sú flagy:

  • AF_INET — znamená, že formát adresy bude IP: port. Môže byť aj AF_UNIX — kde adresa je zadaná názvom súboru.
  • SOCK_DGRAM — znamená, že prijímame nie "surový paket", ale už prešiel firewalled a s čiastočne orezaným paketom. T.j. dostaneme iba UDP paket bez "fyzickej" obálky UDP paketu. Ak by ste použili flag SOCK_RAW, budete musieť ešte analyzovať aj túto "obalku".

Odosielanie paketov môže byť ako broadcast:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # prepnutí socketu do režimu odosielania broadcastu
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, tak aj na adresu, „odkiaľ paket prišiel“:

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # prepnutí socketu do režimu "mnoho poslucháčov"
                        rz=udp_socket.sendto(packetack, addr)

, kde SOL_SOCKET znamená „úroveň protokolu“ pre nastavenie možností,

, SO_BROADCAST je možnosť, ktorá znamená, že paket posielame ako „broadcast“

  , SO_REUSEADDR je možnosť prepínajúca socket do režimu „mnoho poslucháčov“. Teoreticky nie je potrebná v tomto prípade, ale na jednom zo serverov FreeBSD, na ktorom som testoval, bez tejto možnosti kód nefungoval.

Rozbor paketov DHCP

Tu ma naozaj zaujal Python. Ukazuje sa, že z „krabice“ umožňuje dosť voľne manipulovať s byte kódom. Umožňuje veľmi jednoducho prevádzať na desiatkové hodnoty, reťazce a hex – t.j. to, čo vlastne potrebujeme, aby sme pochopili štruktúru paketu. Napríklad je možné získať rozsah bytov v HEX a jednoducho v bytoch:

    res["xidhex"]=data[4:8].hex()
    res["xidbyte"]=data[4:8]

, zabaliť byty do štruktúry:

res["flags"]=pack('BB',data[10],data[11])

Získať IP z štruktúry:

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"])

To je všetko 😉

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster