DHCP+Mysql strežnik na Pythону

DHCP+Mysql strežnik na Pythону

Cilj ovog projekta bio je:

  • Istraživanje DHCP protokola u IPv4 mreži
  • Učenje Pythona (malo više od nule 😉 )
  • zamena servera DB2DHCP (moj fork), original ovde, koji postaje sve teže kompilirati za novi OS. I ne sviđa mi se što binarni fajl koji ne mogu "promeniti odmah"
  • dobijanje funkcionalnog DHCP servera sa mogućnošću dobijanja IP adrese klijenta po MAC adresi klijenta ili kombinaciji MAC switcha + port (Option 82)
  • pisanje još jednog bicikla (O! to je moja omiljena aktivnost)
  • dobijanje kritika za svoju nespretnost na HabrHabr (ili bolje pozivnicu) 😉

Rezultat: radi 😉 Isprobano na FreeBSD i Ubuntu OS-u. Teoretski, kod se može prilagoditi da radi na bilo kojem OS-u, jer u kodu kao da nema specifičnih zaleđivanja.
Pažnja! Dalje sledi puno.

Link na repozitorij za ljubitelje „da dotaču uživo“.

Proces instalacije, podešavanja i korišćenja rezultata «istraživanja» je mnogo niže, a zatim malo teorije o DHCP protokolu. Za sebe. I za istoriju 😉

Malo teorije

Šta je DHCP

To je mrežni protokol koji omogućava uređaju da sazna svoju IP adresu (kao i druge parametre poput prolaza, DNS-a i slično) od DHCP servera. Razmena paketa se odvija putem UDP protokola. Opšti princip rada uređaja prilikom zahteva za mrežne parametre je sledeći:

  1. Uređaj (klijent) šalje rasprostranjen UDP zahtev (DHCPDISCOVER) širom mreže sa zahtevom "pa neko, dajte mi IP adresu". Obično (ali ne uvek) zahtev dolazi sa porta 68 (izvor), a odredišni port je 67. Neki uređaji šalju pakete i sa porta 67. U paketu DHCPDISCOVER uključena je MAC adresa uređaja klijenta.
  2. Svi DHCP serveri koji se nalaze u mreži (a može ih biti više) formiraju DHCPOFFER ponudu sa mrežnim podešavanjima za uređaj koji je poslao DHCPDISCOVER, i takođe je široko šalju kroz mrežu. Identifikacija kome je paket namenjen ide prema MAC adresi klijenta, koja je prethodno pružena u zahtevu DHCPDISCOVER.
  3. Stranka strankama prodaje DNS usluge, odabire najprivlačniju (kriteriji mogu biti različiti, na primjer vrijeme isporuke paketa, broj prolaznih ruta) i šalje službeni zahtjev DHCPREQUEST s mrežnim postavkama serveru koji mu se svidio. U tom slučaju paket ide konkretnom DHCP serveru.
  4. Server koji primi DHCPREQUEST šalje paket formata DHCPACK, u kojem još jednom navodi mrežne postavke namijenjene tom klijentu.

DHCP+Mysql strežnik na Pythону

Osim toga, postoje paketi DHCPINFORM koji se šalju od klijenta i čija je svrha obavijestiti DHCP server da je "klijent živ" i koristi dodijeljene mrežne postavke. Ovi paketi se u implementaciji ovog servera ignoriraju.

Format paketa

U cjelini, okvir Ethernet paketa izgleda otprilike ovako:

DHCP+Mysql strežnik na Pythону

U našem slučaju razmatraćemo samo podatke unutar paketa UDP, bez zaglavlja protokola razina OSI, a to je struktura DHCP:

DHCPDISCOVER

Dakle, proces dobivanja IP adrese za uređaj započinje tako što DHCP klijent šalje multicast upit s porta 68 na 255.255.255.255:67. U ovom paketu klijent uključuje svoju MAC adresu, kao i ono što zapravo želi dobiti od DHCP servera. Struktura paketa opisana je u tabeli ispod.

Tabela strukture paketa DHCPDISCOVER

Pozicija u paketu
Naziv vrijednosti
Primer
Prikaz
Bajt
Objašnjenje

1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu

2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC

3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja

4
Hops
1
Hex
1
Broj prolaznih ruta

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva

7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese

9
Bootp flags
0
Hex
2
Neki flagevi koji se mogu postaviti kao naznaka parametara protokola

11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)

15
Your client IP address
0.0.0.0
Vrstica
4
IP adresa predložena od strane servera (ako postoji)

19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)

23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)

31
Client hardware address padding
 
Hex
10
Rezervirano mjesto. Obično popunjeno nulama

41
Server host name
 
Vrstica
64
Naziv DHCP servera. Obično se ne šalje

105
Boot file name
 
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju

235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP

DHCP možnosti. Lahko so v katerem koli vrstnem redu

236
Številka možnosti
53
Dec
1
Možnost 53, ki določa vrsto DHCP paketa

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

 
Dolžina možnosti
1
Dec
1

 
Vrednost možnosti
1
Dec
1

 
Številka možnosti
50
Dec
1
Kakšen IP naslov želi prejeti odjemalec

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
172.16.134.61
Vrstica
4

 
Številka možnosti
55
 
1
Zahtevane omrežne nastavitve odjemalca. Sestava je lahko različna

01 — Maska omrežja
03 — Prehod
06 — DNS
oc — Ime gostitelja
0f — ime domene omrežja
1c — naslov za širokoustni (broadcast) poletje
42 — ime TFTP strežnika
79 — Classless Static Route

 
Dolžina možnosti
8
 
1

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

 
Številka možnosti
82
Dec
 
Možnost 82, ki prenaša MAC naslov naprave — retransponderja in nekatera dodatna vrednost.

Najpogosteje — port stikala, na katerem deluje končni DHCP odjemalec. V tej možnosti so 'vključene' dodatne nastavitve. Prvi bajt je številka 'podmožnosti', drugi je njena dolžina, nato pa njena vrednost.

V tem primeru so v možnosti 82 vključene podmožnosti:
Agent Circuit ID = 00:04:00:01:00:04, kjer sta zadnja dva bajta port odjemalca DHCP, s katerega je prišel zahtevek

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC naslov naprave retransponderja DHCP

 
Dolžina možnosti
18
Dec
 

 
Vrednost možnosti
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Konec paketa
255
Dec
1
255 simbolizira konec paketa

DHCPOFFER

Ko strežnik prejme paket DHCPDISCOVER in vidi, da lahko odjemalcu ponudi nekaj iz zahtevanega, oblikuje odgovor — DHCPDISCOVER. Odgovor se pošlje na port 'od koder je prišel', po širokoustnem načinu, saj v tem trenutku odjemalec še nima IP naslova, zato lahko paket sprejme le, če je poslan širokoustno. Odjemalec prepozna, da je to paket zanj po svojem MAC naslovu znotraj paketa, pa tudi po številu transakcije, ki ga generira ob oblikovanju prvega paketa.

Tabela strukture paketa DHCPOFFER

Pozicija u paketu
Ime vrednosti (splošno sprejeto)
Primer
Prikaz
Bajt
Objašnjenje

1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu

2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC

3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja

4
Hops
1
Hex
1
Broj prolaznih ruta

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva

7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese

9
Bootp flags
0
Hex
2
Nekateri zastavice, ki jih je mogoče nastaviti kot označitev parametrov protokola. V tem primeru, 0 — pomeni vrsto zahteve Unicast

11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)

15
Your client IP address
172.16.134.61
Vrstica
4
IP adresa predložena od strane servera (ako postoji)

19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)

23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)

31
Client hardware address padding
 
Hex
10
Rezervirano mjesto. Obično popunjeno nulama

41
Server host name
 
Vrstica
64
Naziv DHCP servera. Obično se ne šalje

105
Boot file name
 
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju

235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP

DHCP možnosti. Lahko so v katerem koli vrstnem redu

236
Številka možnosti
53
Dec
1
Možnost 53, ki določa vrsto DHCP paketa 2 — DHCPOFFER

 
Dolžina možnosti
1
Dec
1

 
Vrednost možnosti
2
Dec
1

 
Številka možnosti
1
Dec
1
Možnost, ki ponuja DHCP odjemalcu masko omrežja

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
255.255.224.0
Vrstica
4

 
Številka možnosti
3
Dec
1
Možnost, ki ponuja DHCP odjemalcu privzeti prehod

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
172.16.12.1
Vrstica
4

 
Številka možnosti
6
Dec
1
Možnost, ki ponuja DHCP odjemalcu DNS

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
8.8.8.8
Vrstica
4

 
Številka možnosti
51
Dec
1
Časovna doba, v kateri so izdane omrežne nastavitve v sekundah, po kateri mora DHCP odjemalec ponovno zahtevati

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
86400
Dec
4

 
Številka možnosti
82
Dec
1
Možnost 82 ponavlja to, kar je prišlo v DHCPDISCOVER

 
Dolžina možnosti
18
Dec
1

 
Vrednost možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Konec paketa
255
Dec
1
255 simbolizira konec paketa

DHCPREQUEST

Ko klient prejme DHCPOFFER, oblikuje paket z zahtevo za omrežne parametre, ne več na vse DHCP strežnike v omrežju, temveč le na enega določenega, katerega DHCPOFFER se mu je najbolj "zdela". Kriteriji "zdela" so lahko različni in so odvisni od implementacije DHCP odjemalca. Prejemnika zahtevka določa MAC naslov DHCP strežnika. Paket DHCPREQUEST lahko odjemalec pošlje tudi brez predhodne oblikovanja DHCPDISCOVER, če je IP naslov strežnika že bil pridobljen prej.

Tabela strukture paketa DHCPREQUEST

Pozicija u paketu
Ime vrednosti (splošno sprejeto)
Primer
Prikaz
Bajt
Objašnjenje

1
Boot Request
1
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu

2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC

3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja

4
Hops
1
Hex
1
Broj prolaznih ruta

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva

7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese

9
Bootp flags
8000
Hex
2
Nekateri zastavice, ki se lahko nastavijo kot indikator parametrov protokola. V tem primeru je nastavljeno "broadcast".

11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)

15
Your client IP address
172.16.134.61
Vrstica
4
IP adresa predložena od strane servera (ako postoji)

19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)

23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)

31
Client hardware address padding
 
Hex
10
Rezervirano mjesto. Obično popunjeno nulama

41
Server host name
 
Vrstica
64
Naziv DHCP servera. Obično se ne šalje

105
Boot file name
 
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju

235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP

DHCP možnosti. Lahko so v katerem koli vrstnem redu

236
Številka možnosti
53
Dec
3
Možnost 53, ki določa tip paketa DHCP 3 — DHCPREQUEST

 
Dolžina možnosti
1
Dec
1

 
Vrednost možnosti
3
Dec
1

 
Številka možnosti
61
Dec
1
ID odjemalca: 01 (za Ethernet) + MAC naslov odjemalca

 
Dolžina možnosti
7
Dec
1

 
Vrednost možnosti
01:2c:ab:25:ff:72:a6
Hex
7

 
Številka možnosti
60
Dec
 
"Vendor class identifier". V mojem primeru sporoča verzijo DHCP odjemalca. Morda druge naprave vračajo nekaj drugega. Windows na primer sporoča MSFT 5.0.

 
Dolžina možnosti
11
Dec
 

 
Vrednost možnosti
udhcp 0.9.8
Vrstica
 

 
Številka možnosti
55
 
1
Zahtevane omrežne nastavitve odjemalca. Sestava je lahko različna

01 — Maska omrežja
03 — Prehod
06 — DNS
oc — Ime gostitelja
0f — ime domene omrežja
1c — naslov za širokoustni (broadcast) poletje
42 — ime TFTP strežnika
79 — Classless Static Route

 
Dolžina možnosti
8
 
1

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

 
Številka možnosti
82
Dec
1
Možnost 82 ponavlja to, kar je prišlo v DHCPDISCOVER

 
Dolžina možnosti
18
Dec
1

 
Vrednost možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Konec paketa
255
Dec
1
255 simbolizira konec paketa

DHCPACK

Kot potrditev, da "da, to je tvoj IP naslov in ga nikomur drugemu ne bom dodelil" od DHCP strežnika, služi paket v formatu DHCPACK od strežnika k odjemalcu. Tako kot ostali paketi se tudi ta pošilja široko. V spodnjem kodi DHCP strežnika, implementiranega v Pythonu, v vsakem primeru podvojim katero koli širokoodmorničko zahtevo, tako da pošljem paket na določen IP odjemalca, če je ta že poznan. Pri tem DHCP strežnik sploh ne zanima, ali je paket DHCPACK prišel do odjemalca. Če odjemalec ne prejme DHCPACK, bo po nekem času preprosto ponovil DHCPREQUEST.

Tabela strukture paketa DHCPACK

Pozicija u paketu
Ime vrednosti (splošno sprejeto)
Primer
Prikaz
Bajt
Objašnjenje

1
Boot Request
2
Hex
1
Tip poruke. 1 — zahtjev od klijenta prema serveru, 2 — odgovor servera klijentu

2
Hardware type
1
Hex
1
Tip hardverske adrese, u ovom protokolu 1 — MAC

3
Hardware address length
6
Hex
1
Duljina MAC adrese uređaja

4
Hops
1
Hex
1
Broj prolaznih ruta

5
Transaction ID
23:cf:de:1d
Hex
4
Jedinstveni identifikator transakcije. Generira ga klijent na početku operacije zahtjeva

7
Second elapsed
0
Hex
4
Vrijeme u sekundama od početka procesa dobivanja adrese

9
Bootp flags
8000
Hex
2
Nekateri zastavice, ki se lahko nastavijo kot indikator parametrov protokola. V tem primeru je nastavljeno "broadcast".

11
Client IP address
0.0.0.0
Vrstica
4
IP adresa klijenta (ako postoji)

15
Your client IP address
172.16.134.61
Vrstica
4
IP adresa predložena od strane servera (ako postoji)

19
Next server IP address
0.0.0.0
Vrstica
4
IP adresa servera (ako je poznata)

23
Relay agent IP address
172.16.114.41
Vrstica
4
IP adresa agenta za premošćivanje (na primjer, preklopnika)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC adresa pošiljatelja paketa (klijenta)

31
Client hardware address padding
 
Hex
10
Rezervirano mjesto. Obično popunjeno nulama

41
Server host name
 
Vrstica
64
Naziv DHCP servera. Obično se ne šalje

105
Boot file name
 
Vrstica
128
Ime datoteke na strežniku, ki ga brezdiskne postaje uporabljajo pri nalaganju

235
Magic cookie
63:82:53:63
Hex
4
«Magical» število, ki omogoča določitev, da ta paket pripada protokolu DHCP

DHCP možnosti. Lahko so v katerem koli vrstnem redu

236
Številka možnosti
53
Dec
3
Možnost 53, ki določa tip paketa DHCP 5 — DHCPACK

 
Dolžina možnosti
1
Dec
1

 
Vrednost možnosti
5
Dec
1

 
Številka možnosti
1
Dec
1
Možnost, ki ponuja DHCP odjemalcu masko omrežja

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
255.255.224.0
Vrstica
4

 
Številka možnosti
3
Dec
1
Možnost, ki ponuja DHCP odjemalcu privzeti prehod

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
172.16.12.1
Vrstica
4

 
Številka možnosti
6
Dec
1
Možnost, ki ponuja DHCP odjemalcu DNS

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
8.8.8.8
Vrstica
4

 
Številka možnosti
51
Dec
1
Časovna doba, v kateri so izdane omrežne nastavitve v sekundah, po kateri mora DHCP odjemalec ponovno zahtevati

 
Dolžina možnosti
4
Dec
1

 
Vrednost možnosti
86400
Dec
4

 
Številka možnosti
82
Dec
1
Možnost 82 ponavlja to, kar je prišlo v DHCPDISCOVER

 
Dolžina možnosti
18
Dec
1

 
Vrednost možnosti
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Konec paketa
255
Dec
1
255 simbolizira konec paketa

Namestitev

Namestitev v bistvu vključuje namestitev potrebnih Python modulov za delovanje. Predpostavlja se, da je MySQL že nameščen in nastavljen.

FreeBSD

pkg install python3
python3 -m ensurepip
pip3 install mysql-connector

Ubuntu

sudo apt-get install python3
sudo apt-get install pip3
sudo pip3 install mysql-connector

Ustvarimo MySQL bazo podatkov, naložimo vanjo dump pydhcp.sql, nastavimo konfiguracijsko datoteko.

Конфигурация

Vse nastavitve strežnika so v datoteki v formatu xml. Referenčna datoteka:

0.0.0.0
        255.255.255.255
        192.168.0.71
	8600
	1
        255.255.255.0
        192.168.0.1
        8.8.8.8
    
    
        localhost
	test
	test
	pydhcp
    
    
       option_82_hex:sw_port1:20:22       
       option_82_hex:sw_port2:16:18       
       option_82_hex:sw_mac:26:40
    
    
        3
	select ip,mask,router,dns from users where upper(mac)=upper('{option_82_AgentRemoteId_hex}') and upper(port)=upper('{option_82_AgentCircuitId_port_hex}')
        select ip,mask,router,dns from users where upper(mac)=upper('{sw_mac}') and upper(port)=upper('{sw_port2}')
        select ip,mask,router,dns from users where upper(mac)=upper('{ClientMacAddress}')
	insert into history (id,dt,mac,ip,comment) values (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')

Zdajmo podrobneje o oznakah:

Odsek dhcpserver opisuje osnovne nastavitve za zagon strežnika, in sicer:

  • host — kateri IP naslov strežnik posluša na priključku 67
  • broadcast — kateri IP je broadcast za DHCPOFFER in DHCPACK
  • DHCPServer — kateri IP ima DHCP strežnik
  • LeaseTime — čas najema dodeljenega IP naslova
  • ThreadLimit — koliko hkratnih niti je zagnanih za obdelavo prejetih UDP paketov na priključku 67. Predvideva se, da bo to pomagalo pri projektih z visoko obremenitvijo 😉
  • defaultMask, defaultRouter, defaultDNS — to, kar se ponudi naročniku privzeto, če je IP najden v bazi, vendar dodatni parametri zanj niso navedeni

Odsek mysql:

host, username, password, basename — vse govori sama zase. Približna struktura podatkovne baze je objavljena na GitHub

Odsek query: tukaj so opisani poizvedbi za pridobitev OFFER/ACK:

  • offer_count — število vrstic z poizvedbami, ki vračajo rezultat v obliki ip, mask, router, dns
  • offer_n — vrstica poizvedbe. Če je vrnitev prazna, izvede naslednjo poizvedbo offer
  • history_sql — poizvedba, ki na primer piše v "zgodovino avtorizacije" naročnika

V poizvedbah lahko sodelujejo poljubne spremenljivke iz odseka options ali možnosti iz protokola DHCP.

Odsek options. Tukaj je že bolj zanimivo. Tu lahko ustvarjamo spremenljivke, ki jih lahko uporabljamo kasneje v odseku query.

Na primer:

option_82_hex:sw_port1:20:22

, ta ukazova komanda vzima vse stringi prišle v DHCP zahtevi, v formatu hex, v razponu od 20 do 22 bajtov vključno in jo postavi v novo spremenljivko sw_port1 (priključek stikala, od koder je prišla zahteva)

option_82_hex:sw_mac:26:40

, določamo spremenljivko sw_mac, tako da vzamemo hex iz razpona 26:40

Vse možne možnosti, ki jih lahko uporabimo v zahtevah, lahko vidimo tako, da zaženemo strežnik s ključem -d. Videli bomo približno takšen log:

--pršel je paket DHCPINFORM na vratih 67, od 0025224ad764, b'x91xa5xe0xa3xa5xa9-x8fx8a', ('172.30.114.25', 68)
{'ClientMacAddress': '0025224ad764',
 'ClientMacAddressByte': b'x00%"Jxd7d',
 'HType': 'Ethernet',
 'HostName': b'x91xa5xe0xa3xa5xa9-x8fx8a',
 'ReqListDNS': True,
 'ReqListDomainName': True,
 'ReqListPerfowmRouterDiscover': True,
 'ReqListRouter': True,
 'ReqListStaticRoute': True,
 'ReqListSubnetMask': True,
 'ReqListVendorSpecInfo': 43,
 'RequestedIpAddress': '0.0.0.0',
 'Vendor': b'MSFT 5.0',
 'chaddr': '0025224ad764',
 'ciaddr': '172.30.128.13',
 'flags': b'x00x00',
 'giaddr': '172.30.114.25',
 'gpoz': 308,
 'hlen': 6,
 'hops': 1,
 'htype': 'MAC',
 'magic_cookie': b'cx82Sc',
 'op': 'DHCPINFORM',
 'option12': 12,
 'option53': 53,
 'option55': 55,
 'option60': 60,
 'option61': 61,
 'option82': 82,
 'option_82_byte': b'x12x01x06x00x04x00x01x00x06x02x08x00'
                   b'x06x00x1eXx9exb2xad',
 'option_82_hex': '12010600040001000602080006001e589eb2ad',
 'option_82_len': 18,
 'option_82_str': "b'x12x01x06x00x04x00x01x00x06x02x08x00x06x00x1eXx9exb2xad'",
 'result': False,
 'secs': 768,
 'siaddr': '0.0.0.0',
 'sw_mac': '001e589eb2ad',
 'sw_port1': '06',
 'xidbyte': b'<x89}x8c',
 'xidhex': '3c897d8c',
 'yiaddr': '0.0.0.0'}

Zato lahko katero koli spremenljivko ovijemo v {} in bo uporabljena v SQL zahtevi.

Zabeležimo za zgodovino, da je odjemalec prejel IP naslov:

DHCP+Mysql strežnik na Pythону

DHCP+Mysql strežnik na Pythону

Zagon strežnika

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

— d način prikaza v konzoli DEBUG
— c <ime_datoteke> konfiguracijska datoteka

Analiza

Zdaj pa podrobneje o implementaciji strežnika v Pythonu. To je bolečina. Python je bil učen 'v teku'. Mnogi trenutki so bili narejeni v stilu: 'uhu, nekako sem naredil, da deluje'. Sploh niso optimizirani in so ostali v tem stanju predvsem zaradi malo izkušenj v razvoju na Pythonu. Osredotočil se bom na najbolj zanimive trenutke implementacije strežnika v 'kodu'.

Parser za datoteko z nastavitvami XML

Uporablja se standardni modul Python xml.dom. Zdi se preprosto, vendar pri implementaciji močno primanjkuje kakovostne dokumentacije in primerov na spletu, ki bi uporabljali ta modul.

    tree = minidom.parse(gconfig["config_file"])
    mconfig=tree.getElementsByTagName("mysql")
    for elem in mconfig:        
        gconfig["mysql_host"]=elem.getElementsByTagName("host")[0].firstChild.data      
        gconfig["mysql_username"]=elem.getElementsByTagName("username")[0].firstChild.data      
        gconfig["mysql_password"]=elem.getElementsByTagName("password")[0].firstChild.data      
        gconfig["mysql_basename"]=elem.getElementsByTagName("basename")[0].firstChild.data      
    dconfig=tree.getElementsByTagName("dhcpserver")
    for elem in dconfig:        
        gconfig["broadcast"]=elem.getElementsByTagName("broadcast")[0].firstChild.data      
        gconfig["dhcp_host"]=elem.getElementsByTagName("host")[0].firstChild.data      
        gconfig["dhcp_LeaseTime"]=elem.getElementsByTagName("LeaseTime")[0].firstChild.data      
        gconfig["dhcp_ThreadLimit"]=int(elem.getElementsByTagName("ThreadLimit")[0].firstChild.data)              
        gconfig["dhcp_Server"]=elem.getElementsByTagName("DHCPServer")[0].firstChild.data              
        gconfig["dhcp_defaultMask"]=elem.getElementsByTagName("defaultMask")[0].firstChild.data              
        gconfig["dhcp_defaultRouter"]=elem.getElementsByTagName("defaultRouter")[0].firstChild.data              
        gconfig["dhcp_defaultDNS"]=elem.getElementsByTagName("defaultDNS")[0].firstChild.data              
    qconfig=tree.getElementsByTagName("query")
    for elem in qconfig:  
        gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data                          
        for num in range(int(gconfig["offer_count"])):
            gconfig["offer_"+str(num+1)]=elem.getElementsByTagName("offer_"+str(num+1))[0].firstChild.data      
        gconfig["history_sql"]=elem.getElementsByTagName("history_sql")[0].firstChild.data                          
    options=tree.getElementsByTagName("options")       
    for elem in options:          
        node=elem.getElementsByTagName("option")
        for options in node:
            optionsMod.append(options.firstChild.data)

Večnitnost

Необычно, но многопоточность в Python реализована достаточно просто и понятно.

def PacketWork(data, addr): 
...
# реализация разбора пришедшего пакета и ответа на него
...


while True:
    data, addr = udp_socket.recvfrom(1024) # ждем пакет UDP
    thread = threading.Thread(target=PacketWork, args=(data, addr,)).start()	# как только пришел, запускаем в фоне заранее определенную функцию PacketWork с параметрами
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # если количество запущенных потоков превышает настройку, ждем, пока их станет меньше

Прием/отправка пакета DHCP

Чтобы перехватить UDP-пакеты, проходящие через сетевую карту, нужно «поднять» сокет:

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

, где флаги:

  • AF_INET — обозначает, что формат адреса будет IP:порт. Существуют также AF_UNIX — где адрес задается именем файла.
  • SOCK_DGRAM — означает, что мы принимаем не «сырой пакет», а уже прошедший через файрвол, и с частично обрезанным пакетом. То есть, мы получаем только UDP-пакет без «физической» части обертки пакета UDP. Если использовать флаг SOCK_RAW, то придется также парсить эту «обертку».

Pošiljanje paketa je lahko bodisi multicast:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # preklopimo soket v način pošiljanja multicast
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, bodisi na naslov, «od koder je prišel paket»:

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # preklopimo soket v način "več poslušalcev"
                        rz=udp_socket.sendto(packetack, addr)

, kjer SOL_SOCKET pomeni «nivo protokola» za nastavitev možnosti,

, SO_BROADCAST možnost, ki pošilja paket «multicast»

  , SO_REUSEADDR možnost, ki soket preklopi v način «več poslušalcev». V teoriji ni potrebna v tem primeru, vendar na eni od strežnikov FreeBSD, kjer sem izvajal teste, brez te možnosti koda ni delovala.

Razčlenitev paketa DHCP

Tukaj me je Python resnično navdušil. Izkazalo se je, da omogoča precej prosto delo z byte-kodom. Omogoča zelo preprosto pretvorbo v decimalne vrednosti, nize in hex — torej to, kar pravzaprav potrebujemo, da razumemo strukturo paketa. Tako lahko na primer dobimo obseg byte-ov v HEX in preprostih byte-ih:

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

, zložiti byte-e v strukturo:

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

Pridobiti IP iz strukture:

res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));

In obratno:

res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])

To je to vse 😉

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster