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
Client IP address
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
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
снежні
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
Client IP address
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
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
снежні
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
Client IP address
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
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
снежні
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
Client IP address
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
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
снежні
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 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': '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_by 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].firstChil 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_de 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") : 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

Дадаць каментар