DHCP + Mysql сървър на Python

DHCP + Mysql сървър на Python

Целта на този проект беше:

  • Изучаване на протокола DHCP при работа в мрежа IPv4
  • Изучаване на Python (малко повече от нулата 😉 )
  • замяна на сървъра DB2DHCP (моят форк), оригинал тук., който става все по-труден за компилиране под новата ОС. И не ми харесва, че бинарникът не може да бъде „сменен веднага“
  • постигане на работещ сървър DHCP с възможност за избор на IP адрес на абоната по mac адреса на абоната или комбинацията mac адрес на суича + порт (Option 82)
  • написване на поредния велосипед (О! Това е любимото ми занимание)
  • получаване на критики за моята несръчност в Хабрахабр (по-добре да е инвайт) 😉

Резултат: работи 😉 Тестирано на ОС FreeBSD и Ubuntu. Теоретично кодът може да бъде накаран да работи под всяка ОС, тъй като в него няма специфични зависимости.
Внимание! Напред следва много.

Линк към репозитория за любопитните „да пипнете на живо“.

Процесът на инсталиране, настройване и използване на резултата от „изучаването на материята“ е много по-долу, а след това малко теория за протокола DHCP. За себе си. И за историята 😉

Малко теория

Какво е DHCP

Това е мрежов протокол, който позволява на устройството да научи своя IP адрес (както и други параметри като шлюз, DNS и т.н.) от DHCP сървъра. Обменът на пакети се осъществява по UDP протокол. Общият принцип на работа на устройството при запитване за параметри на мрежата е следният:

  1. Устройството (клиент) разпространява широковещателно UDP запитване (DHCPDISCOVER) в мрежата с искане "някой, дайте ми IP адрес". Обикновено (но не винаги) запитването се прави от порт 68 (източник), а назначението е порт 67 (приемник). Някои устройства изпращат пакети и от порт 67. В пакета DHCPDISCOVER е включен MAC адресът на клиента.
  2. Всички DHCP сървъри, намиращи се в мрежата (а те могат да бъдат няколко), формулират предложение DHCPOFFER с мрежови настройки за устройството, изпратило DHCPDISCOVER, и го изпращат в мрежата. Идентификацията на кому е предназначен този пакет се извършва по MAC адреса на клиента, предоставен в запитването DHCPDISCOVER.
  3. Клиентът получава пакети с предложения за мрежови настройки, избира най-привлекателния (критериите могат да бъдат различни, включително времето за доставка на пакета, броя на междинните маршрути) и изпраща на предпочитания DHCP сървър 'офциална заявка' DHCPREQUEST със сетевите настройки. В този случай пакетът отива към конкретния DHCP сървър.
  4. Сървърът, получил DHCPREQUEST, изпраща пакет с формат DHCPACK, в който отново изброява мрежовите настройки, предназначени за този клиент.

DHCP + Mysql сървър на Python

Съществуват също пакети DHCPINFORM, които се изпращат от клиента и целят да информират DHCP сървъра, че 'клиентът е активен' и използва предоставените мрежови настройки. При реализирането на този сървър, тези пакети се игнорират.

Формат на пакетите

В общи линии, фреймът на Ethernet пакета изглежда приблизително така:

DHCP + Mysql сървър на Python

В нашия случай ще разгледаме само данните, непосредствено съдържащи се в UDP пакета, без хедерите на протоколите от OSI слоевете, именно структурата на DHCP:

DHCPDISCOVER

И така, процесът на получаване на IP адрес за устройството започва с това, че клиентът DHCP изпраща широковещателно искане от порт 68 на 255.255.255.255:67. В този пакет клиентът включва своя MAC адрес, както и какво именно иска да получи от DHCP сървъра. Структурата на пакета е описана в таблицата по-долу.

Таблица на структурата на пакета DHCPDISCOVER

Позиция в пакета
Наименование на стойността
Пример
Представяне
Байт
Обяснение

1
Boot Request
1
Hex
1
Тип съобщение. 1 — заявка от клиента към сървъра, 2 — отговор от сървъра към клиента

2
Hardware type
1
Hex
1
Тип на хардуерния адрес, в този протокол 1 — MAC

3
Hardware address length
6
Hex
1
Дължина на MAC адреса на устройството

4
Hops
1
Hex
1
Брой междинни маршрути

5
Transaction ID
23:cf:de:1d
Hex
4
Уникален идентификатор на транзакцията. Генерира се от клиента в началото на процеса на искане

7
Second elapsed
0
Hex
4
Време в секунди от началото на процеса за получаване на адрес

9
Bootp flags
0
Hex
2
Някои флагове, които могат да бъдат зададени за указване на параметри на протокола

11
Client IP address
0.0.0.0
Редът
4
IP адрес на клиента (ако има)

15
Вашият IP адрес на клиента
0.0.0.0
Редът
4
IP адрес, предложен от сървъра (ако има)

19
Next server IP address
0.0.0.0
Редът
4
IP адрес на сървъра (ако е известен)

23
Relay agent IP address
172.16.114.41
Редът
4
IP адрес на агента за ретранслация (например комутатор)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC адрес на изпращача на пакета (клиента)

31
Client hardware address padding
 
Hex
10
Резервирано място. Обикновено запълнено с нули

41
Server host name
 
Редът
64
Име на DHCP сървъра. Обикновено не се предава

105
Boot file name
 
Редът
128
Име на файл на сървъра, използвано от бездискови станции при зареждане

235
Magic cookie
63:82:53:63
Hex
4
«Магическо» число, с което може да се определи, че този пакет принадлежи на протокола DHCP

DHCP опции. Могат да бъдат в произволен ред

236
Номер на опцията
53
Dec
1
Опция 53, определяща типа на пакета DHCP

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

 
Дължина на опцията
1
Dec
1

 
Стойност на опцията
1
Dec
1

 
Номер на опцията
50
Dec
1
Какъв IP адрес иска да получи клиентът

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
172.16.134.61
Редът
4

 
Номер на опцията
55
 
1
Запрашвани от клиента мрежови параметри. Съставът може да бъде различен

01 — Мрежова маска
03 — Шлюз
06 — DNS
oc — Име на хоста
0f — име на домейна на мрежата
1c — адрес на широковещателния запит (броадкаст)
42 — име на TFTP сървъра
79 — безкласов статичен маршрут

 
Дължина на опцията
8
 
1

 
Стойност на опцията
01:03:06:0c:0f:1c:42:79
 
8

 
Номер на опцията
82
Dec
 
Опция 82, в която се предава MAC адресът на устройството — ретранслатор и някакви допълнителни стойности.

Най-често — порт на суича, на който работи крайният клиент DHCP. В тази опция "вложени" са допълнителни параметри. Първият байт е номер на "подопция", вторият ѝ дължина, а след това е нейното значение.

В този случай в опция 82, са вложени подопции:
Agent Circuit ID = 00:04:00:01:00:04, където последните два байта са портът на клиента DHCP, от който е дошла заявката

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC адресът на устройството ретранслатор DHCP

 
Дължина на опцията
18
Dec
 

 
Стойност на опцията
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Краят на пакета
255
Dec
1
255 символизира край на пакета

DHCPOFFER

Когато сървърът получи пакета DHCPDISCOVER и ако види, че може да предложи нещо на клиента от заявеното, той формулира отговор — DHCPDISCOVER. Отговорът се изпраща на порта, от който е дошъл, чрез броадкаст, тъй като в този момент клиентът все още няма IP адрес и следователно пакетът може да бъде приет само ако е изпратен широковещателно. Клиентът разпознава, че това е пакет за него по своя MAC адрес в пакета, а също и по номера на транзакцията, който генерира в момента на създаване на първия пакет.

Маса на структурата на пакета DHCPOFFER

Позиция в пакета
Име на стойността (общоприето)
Пример
Представяне
Байт
Обяснение

1
Boot Request
1
Hex
1
Тип съобщение. 1 — заявка от клиента към сървъра, 2 — отговор от сървъра към клиента

2
Hardware type
1
Hex
1
Тип на хардуерния адрес, в този протокол 1 — MAC

3
Hardware address length
6
Hex
1
Дължина на MAC адреса на устройството

4
Hops
1
Hex
1
Брой междинни маршрути

5
Transaction ID
23:cf:de:1d
Hex
4
Уникален идентификатор на транзакцията. Генерира се от клиента в началото на процеса на искане

7
Second elapsed
0
Hex
4
Време в секунди от началото на процеса за получаване на адрес

9
Bootp flags
0
Hex
2
Някакви флагове, които могат да бъдат установени, като указания за параметрите на протокола. В този случай, 0 — означава тип на заявка Unicast

11
Client IP address
0.0.0.0
Редът
4
IP адрес на клиента (ако има)

15
Вашият IP адрес на клиента
172.16.134.61
Редът
4
IP адрес, предложен от сървъра (ако има)

19
Next server IP address
0.0.0.0
Редът
4
IP адрес на сървъра (ако е известен)

23
Relay agent IP address
172.16.114.41
Редът
4
IP адрес на агента за ретранслация (например комутатор)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC адрес на изпращача на пакета (клиента)

31
Client hardware address padding
 
Hex
10
Резервирано място. Обикновено запълнено с нули

41
Server host name
 
Редът
64
Име на DHCP сървъра. Обикновено не се предава

105
Boot file name
 
Редът
128
Име на файл на сървъра, използвано от бездискови станции при зареждане

235
Magic cookie
63:82:53:63
Hex
4
«Магическо» число, с което може да се определи, че този пакет принадлежи на протокола DHCP

DHCP опции. Могат да бъдат в произволен ред

236
Номер на опцията
53
Dec
1
Опция 53, определяща типа на пакета DHCP 2 — DHCPOFFER

 
Дължина на опцията
1
Dec
1

 
Стойност на опцията
2
Dec
1

 
Номер на опцията
1
Dec
1
Опция, предлагаща на DHCP клиента мрежова маска

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
255.255.224.0
Редът
4

 
Номер на опцията
3
Dec
1
Опция, предлагаща на DHCP клиента шлюз по подразбиране

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
172.16.12.1
Редът
4

 
Номер на опцията
6
Dec
1
Опция, предлагаща на DHCP клиента DNS

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
8.8.8.8
Редът
4

 
Номер на опцията
51
Dec
1
Времето за живот на предоставените мрежови параметри в секунди, след което DHCP клиентът трябва да ги запита отново

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
86400
Dec
4

 
Номер на опцията
82
Dec
1
Опция 82, повтаря това, което е дошло в DHCPDISCOVER

 
Дължина на опцията
18
Dec
1

 
Стойност на опцията
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Краят на пакета
255
Dec
1
255 символизира край на пакета

DHCPREQUEST

След като клиентът получи DHCPOFFER, той формулира пакет с искане на мрежови параметри не към всички DHCP сървъри в мрежата, а само към един конкретен, предложението DHCPOFFER на който му е най-«харесало». Критериите за «харесало» могат да бъдат различни и зависят от реализацията на DHCP клиента. Получателят на заявката се указва с MAC адреса на DHCP сървъра. Също така пакетът DHCPREQUEST може да бъде изпратен от клиента и без предварително генериране на DHCPDISCOVER, ако IP адресът от сървъра вече е бил получен по-рано.

Таблица на структурата на пакета DHCPREQUEST

Позиция в пакета
Име на стойността (общоприето)
Пример
Представяне
Байт
Обяснение

1
Boot Request
1
Hex
1
Тип съобщение. 1 — заявка от клиента към сървъра, 2 — отговор от сървъра към клиента

2
Hardware type
1
Hex
1
Тип на хардуерния адрес, в този протокол 1 — MAC

3
Hardware address length
6
Hex
1
Дължина на MAC адреса на устройството

4
Hops
1
Hex
1
Брой междинни маршрути

5
Transaction ID
23:cf:de:1d
Hex
4
Уникален идентификатор на транзакцията. Генерира се от клиента в началото на процеса на искане

7
Second elapsed
0
Hex
4
Време в секунди от началото на процеса за получаване на адрес

9
Bootp flags
8000
Hex
2
Някои флагове, които могат да се задават като указание за параметри на протокола. В този случай е зададено «broadcast»

11
Client IP address
0.0.0.0
Редът
4
IP адрес на клиента (ако има)

15
Вашият IP адрес на клиента
172.16.134.61
Редът
4
IP адрес, предложен от сървъра (ако има)

19
Next server IP address
0.0.0.0
Редът
4
IP адрес на сървъра (ако е известен)

23
Relay agent IP address
172.16.114.41
Редът
4
IP адрес на агента за ретранслация (например комутатор)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC адрес на изпращача на пакета (клиента)

31
Client hardware address padding
 
Hex
10
Резервирано място. Обикновено запълнено с нули

41
Server host name
 
Редът
64
Име на DHCP сървъра. Обикновено не се предава

105
Boot file name
 
Редът
128
Име на файл на сървъра, използвано от бездискови станции при зареждане

235
Magic cookie
63:82:53:63
Hex
4
«Магическо» число, с което може да се определи, че този пакет принадлежи на протокола DHCP

DHCP опции. Могат да бъдат в произволен ред

236
Номер на опцията
53
Dec
3
Опция 53, определяща типа на пакета DHCP 3 — DHCPREQUEST

 
Дължина на опцията
1
Dec
1

 
Стойност на опцията
3
Dec
1

 
Номер на опцията
61
Dec
1
Идентификатор на клиента: 01 (за Ethernet) + MAC адрес на клиента

 
Дължина на опцията
7
Dec
1

 
Стойност на опцията
01:2c:ab:25:ff:72:a6
Hex
7

 
Номер на опцията
60
Dec
 
«Vendor class identifier». В моя случай съобщава версията на DHCP клиента. Вероятно други устройства връщат нещо различно. Windows например съобщава MSFT 5.0

 
Дължина на опцията
11
Dec
 

 
Стойност на опцията
udhcp 0.9.8
Редът
 

 
Номер на опцията
55
 
1
Запрашвани от клиента мрежови параметри. Съставът може да бъде различен

01 — Мрежова маска
03 — Шлюз
06 — DNS
oc — Име на хоста
0f — име на домейна на мрежата
1c — адрес на широковещателния запит (броадкаст)
42 — име на TFTP сървъра
79 — безкласов статичен маршрут

 
Дължина на опцията
8
 
1

 
Стойност на опцията
01:03:06:0c:0f:1c:42:79
 
8

 
Номер на опцията
82
Dec
1
Опция 82, повтаря това, което е дошло в DHCPDISCOVER

 
Дължина на опцията
18
Dec
1

 
Стойност на опцията
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Краят на пакета
255
Dec
1
255 символизира край на пакета

DHCPACK

Като потвърждение, че «да точно, това е твой IP адрес и повече няма да го дам на никого» от DHCP сървъра служи пакетът в формат DHCPACK от сървъра към клиента. Той, както и другите пакети, се изпраща на широковещателно. Въпреки това, в по-долу представения код на DHCP сървър, реализиран на Python, за всеки случай дублирам всяко широковещателно запитване, изпращайки пакет на конкретния IP на клиента, ако той вече е известен. Освен това, DHCP сървърът съвсем не се интересува дали пакетът DHCPACK е достигнал клиента. Ако клиентът не получи DHCPACK, той просто повтаря DHCPREQUEST след известно време.

Таблица на структурата на пакета DHCPACK

Позиция в пакета
Име на стойността (общоприето)
Пример
Представяне
Байт
Обяснение

1
Boot Request
2
Hex
1
Тип съобщение. 1 — заявка от клиента към сървъра, 2 — отговор от сървъра към клиента

2
Hardware type
1
Hex
1
Тип на хардуерния адрес, в този протокол 1 — MAC

3
Hardware address length
6
Hex
1
Дължина на MAC адреса на устройството

4
Hops
1
Hex
1
Брой междинни маршрути

5
Transaction ID
23:cf:de:1d
Hex
4
Уникален идентификатор на транзакцията. Генерира се от клиента в началото на процеса на искане

7
Second elapsed
0
Hex
4
Време в секунди от началото на процеса за получаване на адрес

9
Bootp flags
8000
Hex
2
Някои флагове, които могат да се задават като указание за параметри на протокола. В този случай е зададено «broadcast»

11
Client IP address
0.0.0.0
Редът
4
IP адрес на клиента (ако има)

15
Вашият IP адрес на клиента
172.16.134.61
Редът
4
IP адрес, предложен от сървъра (ако има)

19
Next server IP address
0.0.0.0
Редът
4
IP адрес на сървъра (ако е известен)

23
Relay agent IP address
172.16.114.41
Редът
4
IP адрес на агента за ретранслация (например комутатор)

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC адрес на изпращача на пакета (клиента)

31
Client hardware address padding
 
Hex
10
Резервирано място. Обикновено запълнено с нули

41
Server host name
 
Редът
64
Име на DHCP сървъра. Обикновено не се предава

105
Boot file name
 
Редът
128
Име на файл на сървъра, използвано от бездискови станции при зареждане

235
Magic cookie
63:82:53:63
Hex
4
«Магическо» число, с което може да се определи, че този пакет принадлежи на протокола DHCP

DHCP опции. Могат да бъдат в произволен ред

236
Номер на опцията
53
Dec
3
Опция 53, определяща типа на пакета DHCP 5 — DHCPACK

 
Дължина на опцията
1
Dec
1

 
Стойност на опцията
5
Dec
1

 
Номер на опцията
1
Dec
1
Опция, предлагаща на DHCP клиента мрежова маска

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
255.255.224.0
Редът
4

 
Номер на опцията
3
Dec
1
Опция, предлагаща на DHCP клиента шлюз по подразбиране

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
172.16.12.1
Редът
4

 
Номер на опцията
6
Dec
1
Опция, предлагаща на DHCP клиента DNS

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
8.8.8.8
Редът
4

 
Номер на опцията
51
Dec
1
Времето за живот на предоставените мрежови параметри в секунди, след което DHCP клиентът трябва да ги запита отново

 
Дължина на опцията
4
Dec
1

 
Стойност на опцията
86400
Dec
4

 
Номер на опцията
82
Dec
1
Опция 82, повтаря това, което е дошло в DHCPDISCOVER

 
Дължина на опцията
18
Dec
1

 
Стойност на опцията
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
Краят на пакета
255
Dec
1
255 символизира край на пакета

Инсталиране

Настройката всъщност се състои в инсталирането на необходимите модули на Python за работа. Предполага се, че MySQL вече е инсталиран и настроен.

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

Създаваме база данни MySQL, качваме в нея дамп pydhcp.sql и конфигурираме конфигурационния файл.

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

Всички настройки на сървъра са записани в файл с формат xml. Еталонен файл:

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

Сега подробно за етикетите:

Секцията dhcpserver описва основните настройки за стартиране на сървъра, а именно:

  • host — на кой ip адрес слуша сървърът на порт 67
  • broadcast — кой ip е бродкаст за DHCPOFFER и DHCPACK
  • DHCPServer — кой ip има DHCP сървърът
  • LeaseTime времето на наемане на предоставения ip адрес
  • ThreadLimit — колко едновременно потока е стартирано за обработка на постъпващите UDP пакети на порт 67. Предполага се, че ще помогне при високо натоварени проекти 😉
  • defaultMask,defaultRouter,defaultDNS — това, което се предлага на абоната по подразбиране, ако IP в базата е намерен, но допълнителни параметри за него не са посочени.

Секция mysql:

host,username,password,basename — всичко говори само за себе си. Примерната структура на базата данни е изложена на GitHub

Секция query: тук се описват заявките за получаване на OFFER/ACK:

  • offer_count — броят на редовете със заявки, които връщат резултати от вида ip,mask,router,dns
  • offer_n — редът на заявката. Ако връщането е празно, се изпълнява следващата заявка offer
  • history_sql — заявка, която пише например в 'историята на авторизацията' на абоната

В заявките могат да участват всякакви променливи от секцията options или опции от протокола DHCP.

Секция options. Тук става по-интересно. Тук можем да създаваме променливи, които можем да използваме по-нататък в секцията query.

Например:

option_82_hex:sw_port1:20:22

, тази команда взема цялата низка, изпратена в DHCP заявката с опция 82, в hex формат, в диапазона от 20 до 22 байта включително и я поставя в нова променлива sw_port1 (порт на суича, от който е дошла заявката)

option_82_hex:sw_mac:26:40

, определяме променливата sw_mac, вземайки hex от диапазона 26:40

Можете да видите всички възможни опции, които могат да се използват в заявките, като стартирате сървера с ключа -d. Ще видим приблизително такъв лог:

--пристигна пакет DHCPINFORM на порт 67, от 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'}

Следователно, можем да обгърнем всяка променлива в {} и тя ще бъде използвана в SQL заявката.

Запечатваме за историята, че клиентът е получил IP адрес:

DHCP + Mysql сървър на Python

DHCP + Mysql сървър на Python

Стартиране на сървъра

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

— d режим за извеждане в конзола DEBUG
— c <името_на_файла> конфигурационен файл

Разбор на полети

А сега по-подробно за реализацията на сървера на Python. Това е трудно. Python беше изучаван 'на лету'. Много неща са направени в стил: 'ухти, направих нещо, което работи'. Напълно не оптимизирани и оставени в такъв вид основно поради малкия опит в разработването на python. Ще се спра на най-интересните моменти от реализацията на сървера в 'кода'.

Парсер на конфигурационния файл XML

Използва се стандартният модул Python xml.dom. Изглежда просто, но при реализирането осезаемо не достига качествена документация и примери в мрежата с използване на този модул.

    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)

Многопоточност

Неразбираемо, многопоточността в 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, ще трябва да парсите и "тази опаковка".

Изпращането на пакет може да бъде както бродкаст:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # променяме сокета в режим на изпращане на бродкаст
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, така и на адреса, "откъдето е дошъл пакетът":

                        udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # променяме сокета в режим "много слушатели"
                        rz=udp_socket.sendto(packetack, addr)

, където SOL_SOCKET означава "протоколно ниво" за настройка на опции,

, SO_BROADCAST е опция, която указва, че пакетът се изпраща "бродкастом"

  , SO_REUSEADDR е опция, която променя сокета в режим "много слушатели". По идеята тя не е нужна в този случай, но на един от сървърите FreeBSD, на който тествам, без тази опция кодът не работеше.

Разбор на пакета DHCP

Тук наистина ми хареса Python. Оказва се, че "от кутията" той позволява доста свободно да се работи с байт-кода. Позволявайки му много лесно да се превръща в десетични стойности, низове и hex — т.е. това, което всъщност ни трябва, за да разберем структурата на пакета. Например, можем да получим диапазон от байтове в HEX и просто байтове:

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

, да опаковаме байтовете в структура:

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

Да получим IP от структурата:

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

И обратно:

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

На това всичко 😉

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster