
Целта на този проект беше:
- Изучаване на протокола DHCP при работа в мрежа IPv4
- Изучаване на Python (малко повече от нулата 😉 )
- замяна на сървъра (моят форк), оригинал , който става все по-труден за компилиране под новата ОС. И не ми харесва, че бинарникът не може да бъде „сменен веднага“
- постигане на работещ сървър DHCP с възможност за избор на IP адрес на абоната по mac адреса на абоната или комбинацията mac адрес на суича + порт (Option 82)
- написване на поредния велосипед (О! Това е любимото ми занимание)
- получаване на критики за моята несръчност в Хабрахабр (по-добре да е инвайт) 😉
Резултат: работи 😉 Тестирано на ОС FreeBSD и Ubuntu. Теоретично кодът може да бъде накаран да работи под всяка ОС, тъй като в него няма специфични зависимости.
Внимание! Напред следва много.
Линк към репозитория за любопитните .
Процесът на инсталиране, настройване и използване на резултата от „изучаването на материята“ е много по-долу, а след това малко теория за протокола DHCP. За себе си. И за историята 😉
Малко теория
Какво е DHCP
Това е мрежов протокол, който позволява на устройството да научи своя IP адрес (както и други параметри като шлюз, DNS и т.н.) от DHCP сървъра. Обменът на пакети се осъществява по UDP протокол. Общият принцип на работа на устройството при запитване за параметри на мрежата е следният:
- Устройството (клиент) разпространява широковещателно UDP запитване (DHCPDISCOVER) в мрежата с искане "някой, дайте ми IP адрес". Обикновено (но не винаги) запитването се прави от порт 68 (източник), а назначението е порт 67 (приемник). Някои устройства изпращат пакети и от порт 67. В пакета DHCPDISCOVER е включен MAC адресът на клиента.
- Всички DHCP сървъри, намиращи се в мрежата (а те могат да бъдат няколко), формулират предложение DHCPOFFER с мрежови настройки за устройството, изпратило DHCPDISCOVER, и го изпращат в мрежата. Идентификацията на кому е предназначен този пакет се извършва по MAC адреса на клиента, предоставен в запитването DHCPDISCOVER.
- Клиентът получава пакети с предложения за мрежови настройки, избира най-привлекателния (критериите могат да бъдат различни, включително времето за доставка на пакета, броя на междинните маршрути) и изпраща на предпочитания DHCP сървър 'офциална заявка' DHCPREQUEST със сетевите настройки. В този случай пакетът отива към конкретния DHCP сървър.
- Сървърът, получил DHCPREQUEST, изпраща пакет с формат DHCPACK, в който отново изброява мрежовите настройки, предназначени за този клиент.

Съществуват също пакети DHCPINFORM, които се изпращат от клиента и целят да информират DHCP сървъра, че 'клиентът е активен' и използва предоставените мрежови настройки. При реализирането на този сървър, тези пакети се игнорират.
Формат на пакетите
В общи линии, фреймът на Ethernet пакета изглежда приблизително така:
![]()
В нашия случай ще разгледаме само данните, непосредствено съдържащи се в 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 — всичко говори само за себе си. Примерната структура на базата данни е изложена на
Секция 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 адрес:


Стартиране на сървъра
.\/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
