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, що знаходяться в мережі (а їх може бути кілька), формують для пристрою DHCPDISCOVER, що відправив, пропозицію DHCPOFFER з мережевими налаштуваннями, і так само широкомовно його відсилає його по мережі. Ідентифікація кому призначений цей пакет йде за 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
Тип апаратного забезпечення
1
Hex
1
Тип апаратної адреси, в даному протоколі 1 MAC

3
Hardware adrees length
6
Hex
1
Довжина MAC адреси пристрою

4
Хміль
1
Hex
1
Кількість проміжних маршрутів

5
Ідентифікатор транзакції
23:cf:de:1d
Hex
4
Унікальний ідентифікатор транзакції. Генерує клієнт на початку операції запиту

7
Second elapsed
0
Hex
4
Час у секундах з початку процесу отримання адреси

9
Bootp flags
0
Hex
2
Деякі прапори, які можуть встановлюватися, як зазначення параметрів протоколу

11
IP-адреса клієнта
0.0.0.0
Рядок
4
IP адреса клієнта (якщо є)

15
Your client IP address
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
MAC-адреса клієнта
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
грудня
1
Опція 53, що визначає тип пакету DHCP

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

 
Довжина опції
1
грудня
1

 
Значення опції
1
грудня
1

 
Номер опції
50
грудня
1
Яку IP адресу хоче отримати клієнт

 
Довжина опції
4
грудня
1

 
Значення опції
172.16.134.61
Рядок
4

 
Номер опції
55
 
1
Мережеві параметри, що запитуються клієнтом. Склад може бути різним

01 - Маска мережі
03 - Шлюз
06 - DNS
oc - Ім'я хоста
0f - ім'я домену мережі
1c - адреса широкомовного запиту (бродкасту)
42 - ім'я сервера TFTP
79 - Classless Static Route

 
Довжина опції
8
 
1

 
Значення опції
01:03:06:0c:0f:1c:42:79
 
8

 
Номер опції
82
грудня
 
Опція 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
грудня
 

 
Значення опції
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Закінчення пакету
255
грудня
1
255 символізує закінчення пакету

DHCPOFFER

Як тільки сервер отримує пакет DHCPDISCOVER і якщо він бачить, що може клієнтові щось запропонувати із запитаного, він формує для нього відповідь — DHCPDISCOVER. Відповідь надсилається порт «звідки прийшов», бродкастом, т.к. в цей момент, у клієнта ще немає IP адреси, отже пакет він може прийняти тільки якщо він відісланий широкомовно. Клієнт розпізнає що це пакет для нього за MAC своєю адресою всередині пакета, а також номер транзакції, який він генерує в момент створення першого пакета.

Таблиця структури пакету DHCPOFFER

Позиція у пакеті
Назва значення (загальне)
Приклад
подання
байт
Пояснення

1
Boot Request
1
Hex
1
Тип повідомлення. 1 - запит від клієнта до сервера, 2 - відповідь від сервера клієнту

2
Тип апаратного забезпечення
1
Hex
1
Тип апаратної адреси, в даному протоколі 1 MAC

3
Hardware adrees length
6
Hex
1
Довжина MAC адреси пристрою

4
Хміль
1
Hex
1
Кількість проміжних маршрутів

5
Ідентифікатор транзакції
23:cf:de:1d
Hex
4
Унікальний ідентифікатор транзакції. Генерує клієнт на початку операції запиту

7
Second elapsed
0
Hex
4
Час у секундах з початку процесу отримання адреси

9
Bootp flags
0
Hex
2
Деякі прапори, які можуть встановлюватися, як зазначення параметрів протоколу. В даному випадку 0 означає тип запиту Unicast

11
IP-адреса клієнта
0.0.0.0
Рядок
4
IP адреса клієнта (якщо є)

15
Your client IP address
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
MAC-адреса клієнта
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
грудня
1
Опція 53, яка визначає тип пакету DHCP 2 - DHCPOFFER

 
Довжина опції
1
грудня
1

 
Значення опції
2
грудня
1

 
Номер опції
1
грудня
1
Опція, що пропонує DHCP клієнту маску мережі

 
Довжина опції
4
грудня
1

 
Значення опції
255.255.224.0
Рядок
4

 
Номер опції
3
грудня
1
Опція, що пропонує DHCP клієнту шлюз за замовчуванням

 
Довжина опції
4
грудня
1

 
Значення опції
172.16.12.1
Рядок
4

 
Номер опції
6
грудня
1
Опція, що пропонує DHCP клієнту DNS

 
Довжина опції
4
грудня
1

 
Значення опції
8.8.8.8
Рядок
4

 
Номер опції
51
грудня
1
Час життя виданих мережевих параметрів за секунди, через який DHCP клієнт повинен запросити їх знову

 
Довжина опції
4
грудня
1

 
Значення опції
86400
грудня
4

 
Номер опції
82
грудня
1
Опція 82, повторює те, що прийшло в DHCPDISCOVER

 
Довжина опції
18
грудня
1

 
Значення опції
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
грудня
18

 
Закінчення пакету
255
грудня
1
255 символізує закінчення пакету

DHCPREQUEST

Після того, як клієнт отримає DHCPOFFER, він формує пакет із запитом мережевих параметрів вже не до всіх серверів DHCP у мережі, а лише до одного конкретного, пропозиція DHCPOFFER якого йому найбільше «сподобалася». Критерії «сподобалося» може бути різні і залежить від реалізації DHCP клієнта. Отримувач запиту вказується за допомогою MAC-адреси сервера DHCP. Також пакет DHCPREQUEST може бути висланий клієнтом і без формування раніше DHCPDISCOVER, якщо IP адреса сервера вже раніше колись була отримана.

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

Позиція у пакеті
Назва значення (загальне)
Приклад
подання
байт
Пояснення

1
Boot Request
1
Hex
1
Тип повідомлення. 1 - запит від клієнта до сервера, 2 - відповідь від сервера клієнту

2
Тип апаратного забезпечення
1
Hex
1
Тип апаратної адреси, в даному протоколі 1 MAC

3
Hardware adrees length
6
Hex
1
Довжина MAC адреси пристрою

4
Хміль
1
Hex
1
Кількість проміжних маршрутів

5
Ідентифікатор транзакції
23:cf:de:1d
Hex
4
Унікальний ідентифікатор транзакції. Генерує клієнт на початку операції запиту

7
Second elapsed
0
Hex
4
Час у секундах з початку процесу отримання адреси

9
Bootp flags
8000
Hex
2
Деякі прапори, які можуть встановлюватися, як зазначення параметрів протокол. В даному випадку виставлено «бродкаст»

11
IP-адреса клієнта
0.0.0.0
Рядок
4
IP адреса клієнта (якщо є)

15
Your client IP address
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
MAC-адреса клієнта
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
грудня
3
Опція 53, яка визначає тип пакету DHCP 3 - DHCPREQUEST

 
Довжина опції
1
грудня
1

 
Значення опції
3
грудня
1

 
Номер опції
61
грудня
1
Ідентифікатор клієнта: 01 (для Ehernet) + MAC адреса клієнта

 
Довжина опції
7
грудня
1

 
Значення опції
01:2c:ab:25:ff:72:a6
Hex
7

 
Номер опції
60
грудня
 
"Vendor class identifier". У моєму випадку відповідає версію DHCP клієнта. Можливо інші пристрої, які повертають щось інше. Windows наприклад повідомляє MSFT 5.0

 
Довжина опції
11
грудня
 

 
Значення опції
udhcp 0.9.8
Рядок
 

 
Номер опції
55
 
1
Мережеві параметри, що запитуються клієнтом. Склад може бути різним

01 - Маска мережі
03 - Шлюз
06 - DNS
oc - Ім'я хоста
0f - ім'я домену мережі
1c - адреса широкомовного запиту (бродкасту)
42 - ім'я сервера TFTP
79 - Classless Static Route

 
Довжина опції
8
 
1

 
Значення опції
01:03:06:0c:0f:1c:42:79
 
8

 
Номер опції
82
грудня
1
Опція 82, повторює те, що прийшло в DHCPDISCOVER

 
Довжина опції
18
грудня
1

 
Значення опції
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
грудня
18

 
Закінчення пакету
255
грудня
1
255 символізує закінчення пакету

DHCPACK

Як підтвердження того, що «так, це твоя IP адреса, і більше я його нікому не видам» від DHCP сервера, служить пакет у форматі DHCPACK від сервера клієнту. Він так само як і інші пакети надсилається широкомовно. Хоча, в наведеному нижче коді DHCP сервера реалізованого на Python, я про всяк випадок дублюю будь-який широкомовний запит, відправкою пакета на конкретний IP клієнта, якщо він вже відомий. Причому DHCP сервер не хвилює, чи дійшов до клієнта пакет DHCPACK. Якщо клієнт не отримує DHCPACK, через деякий час він просто повторює DHCPREQUEST

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

Позиція у пакеті
Назва значення (загальне)
Приклад
подання
байт
Пояснення

1
Boot Request
2
Hex
1
Тип повідомлення. 1 - запит від клієнта до сервера, 2 - відповідь від сервера клієнту

2
Тип апаратного забезпечення
1
Hex
1
Тип апаратної адреси, в даному протоколі 1 MAC

3
Hardware adrees length
6
Hex
1
Довжина MAC адреси пристрою

4
Хміль
1
Hex
1
Кількість проміжних маршрутів

5
Ідентифікатор транзакції
23:cf:de:1d
Hex
4
Унікальний ідентифікатор транзакції. Генерує клієнт на початку операції запиту

7
Second elapsed
0
Hex
4
Час у секундах з початку процесу отримання адреси

9
Bootp flags
8000
Hex
2
Деякі прапори, які можуть встановлюватися, як зазначення параметрів протокол. В даному випадку виставлено «бродкаст»

11
IP-адреса клієнта
0.0.0.0
Рядок
4
IP адреса клієнта (якщо є)

15
Your client IP address
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
MAC-адреса клієнта
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
грудня
3
Опція 53, що визначає тип пакету DHCP 5 - DHCPACK

 
Довжина опції
1
грудня
1

 
Значення опції
5
грудня
1

 
Номер опції
1
грудня
1
Опція, що пропонує DHCP клієнту маску мережі

 
Довжина опції
4
грудня
1

 
Значення опції
255.255.224.0
Рядок
4

 
Номер опції
3
грудня
1
Опція, що пропонує DHCP клієнту шлюз за замовчуванням

 
Довжина опції
4
грудня
1

 
Значення опції
172.16.12.1
Рядок
4

 
Номер опції
6
грудня
1
Опція, що пропонує DHCP клієнту DNS

 
Довжина опції
4
грудня
1

 
Значення опції
8.8.8.8
Рядок
4

 
Номер опції
51
грудня
1
Час життя виданих мережевих параметрів за секунди, через який DHCP клієнт повинен запросити їх знову

 
Довжина опції
4
грудня
1

 
Значення опції
86400
грудня
4

 
Номер опції
82
грудня
1
Опція 82, повторює те, що прийшло в DHCPDISCOVER

 
Довжина опції
18
грудня
1

 
Значення опції
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
грудня
18

 
Закінчення пакету
255
грудня
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. Еталонний файл:

1.0 0.0.0.0 255.255.255.255 192.168.0.71 8600 1 255.255.255.0 192.168.0.1 localhost test test pydhcp option_8.8.8.8_hex:sw_port82:1:20 option_22_hex:sw_port82:2:16 option_18_hex:sw_mac:82:26 40 select ip,mask,router,dns from users where upper(mac)=upper('{option_3_AgentRemoteId_hex}') and upper(port)=upper('{option_1_AgentCircuitId_port_hex}') select ip,mask,router,dns from users where upper(mac)=upper('{sw_mac}') and upper(port)=upper('{sw_port82}') select ip,mask,router,dns from users where upper(mac)=upper('{ClientMacAddress}') insert in historie (id,dt,mac,ip,comment) (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': '0025224 %"Jxd764d', ' HType': 'Ethernet', 'HostName': b'x00xa7xe91xa5xa0xa3-x5fx9a', 'ReqListDNS': True, 'ReqListDomainName': True, 'ReqListPerfowmRouterDiscover': True, 'ReqListRouter': True, 'ReqListRouter': ubnetMask ': True, 'ReqListVendorSpecInfo': 8, 'RequestedIpAddress': '8', 'Vendor': b'MSFT 43', 'chaddr': '0.0.0.0ad5.0', 'ciaddr': '0025224. ': b'x764x172.30.128.13', 'giaddr': '00', 'gpoz': 00, 'hlen': 172.30.114.25, 'hops': 308, 'htype': 'MAC', 'magic_cookie': b'cx6Sc ', 'op': 'DHCPINFORM', 'option1': 82, 'option12': 12, 'option53': 53, 'option55': 55, 'option60': 60, 'option61': 61, 'option_82_ b'x82x82x12x01x06x00x04x00x01x00x06x02' b'x08x00x06eXx00exb1xad', 'option_9_hex': '2 : 82, 'option_12010600040001000602080006001_str': "b'x589x2x82x18x82x12x01x06x00x04x00x01x00x06x02eXx08exb00xad'", 'result': False, 'secs': 06, ' '00', 'sw_mac': '1e9eb2ad', 'sw_port768': '0.0.0.0', 'xidbyte': b'

Відповідно ми можемо будь-яку змінну обернути на {} і вона буде використана в 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") для 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") для 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 dhcp_ThreadLimit"]=int(elem.getElementsByTagName("ThreadLimit")[0].firstChild.data) gconfig["dhcp_Server"]=elem.getElementsByTagName("DHCPServer")[0].firstChild.data =elem.getElementsByTagName("defaultMask")[0].firstChild.data gconfig["dhcp_defaultRouter"]=elem.getElementsByTagName("defaultRouter")[0].firstChild.data gconfig["dhcp_def defaultDNS")[0].firstChild.data qconfig=tree.getElementsByTagName("query") для elem in qconfig: gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data for 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") для 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() вже запущених потоків більше ніж у налаштуваннях, чекаємо поки їх стане менше

Прийом/надсилання пакету 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

Додати коментар або відгук