Python中的DHCP+Mysql伺服器

Python中的DHCP+Mysql伺服器

該項目的目的是:

  • 了解 IPv4 網路上的 DHCP
  • 學習Python(比從頭開始多一點😉)
  • 伺服器更換 DB2DHCP (我的叉子),原創 這裡,為新作業系統組裝變得越來越困難。 我不喜歡它是一個二進位文件,無法“立即更改”
  • 取得工作的 DHCP 伺服器,該伺服器能夠使用訂戶的 MAC 或交換器 MAC+連接埠組合選擇訂戶的 IP 位址(選項 82)
  • 寫另一輛自行車(喔!這是我最喜歡的活動)
  • 在 Habrahabr 上收到有關您的俱樂部慣用手的評論(或者更好的是,邀請)😉

結果:它有效 😉 在 FreeBSD 和 Ubuntu 作業系統上進行了測試。 理論上,可以要求程式碼在任何作業系統下工作,因為程式碼中似乎沒有具體的綁定。
小心! 還有很多事情要做。

連結到業餘愛好者的儲存庫 “觸摸活著”.

安裝、設定和使用「研究硬體」結果的過程就低了很多,然後再講一點關於DHCP協定的理論。 為了我自己。 對於歷史😉

一點理論

什麼是 DHCP

這是一種網路協議,允許設備從 DHCP 伺服器查找其 IP 位址(以及網關、DNS 等其他參數)。 資料包使用 UDP 協定進行交換。 設備請求網路參數時的整體工作原理如下:

  1. 設備(客戶端)在整個網路中發送 UDP 廣播請求 (DHCPDISCOVER),請求「好吧,有人給我一個 IP 位址」。 此外,通常(但並非總是)請求從連接埠 68(來源)發出,目的地是連接埠 67(目的地)。 有些設備也會從連接埠 67 發送資料包。 用戶端設備的 MAC 位址包含在 DHCPDISCOVER 封包內。
  2. 位於網路上的所有 DHCP 伺服器(可能有多個)都會形成一個 DHCPOFFER 報價,其中包含發送 DHCPDISCOVER 的設備的網路設置,並透過網路廣播它。 根據先前在 DHCPDISCOVER 請求中提供的用戶端 MAC 位址來識別此封包的目標使用者。
  3. 用戶端接受帶有網路設定建議的資料包,選擇最有吸引力的一個(標準可能不同,例如資料包傳遞的時間、中間路由的數量),並使用網路設定發出「正式請求」DHCPREQUEST從牠喜歡的DHCP 伺服器。 在這種情況下,封包將會傳送到特定的 DHCP 伺服器。
  4. 收到 DHCPREQUEST 的伺服器發送 DHCPACK 格式的資料包,其中再次列出適用於該客戶端的網路設置

Python中的DHCP+Mysql伺服器

此外,還有來自客戶端的 DHCPINFORM 封包,其目的是通知 DHCP 伺服器「客戶端處於活動狀態」並且正在使用已發佈的網路設定。 在此伺服器的實作中,這些資料包將被忽略。

封裝格式

一般來說,乙太網路資料包框架看起來像這樣:

Python中的DHCP+Mysql伺服器

在我們的例子中,我們將只考慮直接來自 UDP 封包內容的數據,而不考慮 OSI 層協定標頭,即 DHCP 結構:

DHCP發現

因此,取得裝置 IP 位址的過程始於 DHCP 用戶端從連接埠 68 向 255.255.255.255:67 發送廣播請求。 在此封包中,用戶端包含其 MAC 位址,以及它希望從 DHCP 伺服器接收的確切內容。 封裝結構如下表所示。

DHCPDISCOVER封包結構表

在包裝中的位置
值名稱
例子
主意
位元組
澄清

1
啟動請求
1
十六進制
1
訊息類型。 1 - 客戶端向伺服器發出請求,2 - 伺服器向客戶端發出回應

2
硬件類型
1
十六進制
1
硬體位址類型,在此協定中1 - MAC

3
硬體位址長度
6
十六進制
1
設備MAC位址長度

4
酒花
1
十六進制
1
中間路線數量

5
交易紀錄ID
23:比照:德:1d
十六進制
4
唯一的交易識別碼。 由客戶端在請求操作開始時生成

7
秒過去了
0
十六進制
4
從取得地址過程開始的時間(以秒為單位)

9
引導標誌
0
十六進制
2
可以設定某些標誌來指示協定參數

11
客戶端IP位址
0.0.0.0
弦樂
4
客戶端 IP 位址(如果有)

15
您的客戶端IP位址
0.0.0.0
弦樂
4
伺服器提供的 IP 位址(如果可用)

19
下一個伺服器IP位址
0.0.0.0
弦樂
4
伺服器 IP 位址(如果已知)

23
中繼代理IP位址
172.16.114.41
弦樂
4
中繼代理(例如交換器)的IP位址

27
客戶端MAC位址
14:d6:4d:a7:c9:55
十六進制
6
封包發送者(客戶端)的MAC位址

31
用戶端硬體位址填充
 
十六進制
10
預留座位。 通常用零填充

41
伺服器主機名稱
 
弦樂
64
DHCP 伺服器名稱。 通常不傳送

105
啟動檔案名稱
 
弦樂
128
無碟工作站啟動時使用的伺服器上的檔案名

235
魔法餅乾
63:82:53:63
十六進制
4
「神奇」數字,根據該數字,包括。 可以判斷這個封包屬於DHCP協議

DHCP 選項。 可以按任意順序進行

236
選項編號
53
十二月
1
選項53,指定DHCP封包類型

1 - DHCP發現
3 - DHCP 請求
2 - DHCP 報價
5 - DHCPACK
8 - DHCP 訊息

 
選項長度
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 - 無類別靜態路由

 
選項長度
8
 
1

 
選擇權價值
01:03:06:0c:0f:1c:42:79
 
8

 
選項編號
82
十二月
 
選項 82,傳輸中繼器設備的 MAC 位址和一些附加價值。

最常見的是,這是執行最終 DHCP 用戶端的交換器連接埠。此選項包含附加參數。第一個位元組是「子選項」的編號,第二個位元組是其長度,然後是其值。

在本例中,在選項 82 中,子選項是嵌套的:
代理電路 ID = 00:04:00:01:00:04,其中最後兩個位元組是發出請求的 DHCP 用戶端端口

代理遠端 ID = 00:06:c8:be:19:93:11:48 - DHCP 中繼器設備的 MAC 位址

 
選項長度
18
十二月
 

 
選擇權價值
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
十六進制
 

 
包裝結束
255
十二月
1
255表示資料包結束

DHCP提供

一旦伺服器收到 DHCPDISCOVER 封包,並且如果它發現可以向客戶端提供所請求的內容,那麼它就會為其產生一個回應 - DHCPDISCOVER。 回應透過廣播發送到“來自它來的地方”的端口,因為此時客戶端還沒有IP位址,因此只能接受廣播發送的資料包。 客戶端透過包內的 MAC 位址以及創建第一個包時產生的交易號碼來識別這是他的包。

DHCPOFFER封包結構表

在包裝中的位置
值的名稱(通用)
例子
主意
位元組
澄清

1
啟動請求
1
十六進制
1
訊息類型。 1 - 客戶端向伺服器發出請求,2 - 伺服器向客戶端發出回應

2
硬件類型
1
十六進制
1
硬體位址類型,在此協定中1 - MAC

3
硬體位址長度
6
十六進制
1
設備MAC位址長度

4
酒花
1
十六進制
1
中間路線數量

5
交易紀錄ID
23:比照:德:1d
十六進制
4
唯一的交易識別碼。 由客戶端在請求操作開始時生成

7
秒過去了
0
十六進制
4
從取得地址過程開始的時間(以秒為單位)

9
引導標誌
0
十六進制
2
可以設定某些標誌來指示協定參數。 在這種情況下,0表示單播請求類型

11
客戶端IP位址
0.0.0.0
弦樂
4
客戶端 IP 位址(如果有)

15
您的客戶端IP位址
172.16.134.61
弦樂
4
伺服器提供的 IP 位址(如果可用)

19
下一個伺服器IP位址
0.0.0.0
弦樂
4
伺服器 IP 位址(如果已知)

23
中繼代理IP位址
172.16.114.41
弦樂
4
中繼代理(例如交換器)的IP位址

27
客戶端MAC位址
14:d6:4d:a7:c9:55
十六進制
6
封包發送者(客戶端)的MAC位址

31
用戶端硬體位址填充
 
十六進制
10
預留座位。 通常用零填充

41
伺服器主機名稱
 
弦樂
64
DHCP 伺服器名稱。 通常不傳送

105
啟動檔案名稱
 
弦樂
128
無碟工作站啟動時使用的伺服器上的檔案名

235
魔法餅乾
63:82:53:63
十六進制
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
向 DNS 用戶端提供 DHCP 的選項

 
選項長度
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:歐共體
十二月
18

 
包裝結束
255
十二月
1
255表示資料包結束

DHCP請求

用戶端收到 DHCPOFFER 後,會形成一個資料包,不會向網路上的所有 DHCP 伺服器請求網路參數,而僅向他最「喜歡」的特定 DHCP 伺服器請求網路參數。 「喜歡」標準可以不同,並且取決於客戶端的 DHCP 實作。 使用 DHCP 伺服器的 MAC 位址指定請求的接收者。 此外,如果之前已經獲得了伺服器的 IP 位址,則用戶端可以發送 DHCPREQUEST 封包,而無需先生成 DHCPDISCOVER。

DHCPREQUEST封包結構表

在包裝中的位置
值的名稱(通用)
例子
主意
位元組
澄清

1
啟動請求
1
十六進制
1
訊息類型。 1 - 客戶端向伺服器發出請求,2 - 伺服器向客戶端發出回應

2
硬件類型
1
十六進制
1
硬體位址類型,在此協定中1 - MAC

3
硬體位址長度
6
十六進制
1
設備MAC位址長度

4
酒花
1
十六進制
1
中間路線數量

5
交易紀錄ID
23:比照:德:1d
十六進制
4
唯一的交易識別碼。 由客戶端在請求操作開始時生成

7
秒過去了
0
十六進制
4
從取得地址過程開始的時間(以秒為單位)

9
引導標誌
8000
十六進制
2
可以設定某些標誌來指示協定參數。 在這種情況下,設定“廣播”

11
客戶端IP位址
0.0.0.0
弦樂
4
客戶端 IP 位址(如果有)

15
您的客戶端IP位址
172.16.134.61
弦樂
4
伺服器提供的 IP 位址(如果可用)

19
下一個伺服器IP位址
0.0.0.0
弦樂
4
伺服器 IP 位址(如果已知)

23
中繼代理IP位址
172.16.114.41
弦樂
4
中繼代理(例如交換器)的IP位址

27
客戶端MAC位址
14:d6:4d:a7:c9:55
十六進制
6
封包發送者(客戶端)的MAC位址

31
用戶端硬體位址填充
 
十六進制
10
預留座位。 通常用零填充

41
伺服器主機名稱
 
弦樂
64
DHCP 伺服器名稱。 通常不傳送

105
啟動檔案名稱
 
弦樂
128
無碟工作站啟動時使用的伺服器上的檔案名

235
魔法餅乾
63:82:53:63
十六進制
4
「神奇」數字,根據該數字,包括。 可以判斷這個封包屬於DHCP協議

DHCP 選項。 可以按任意順序進行

236
選項編號
53
十二月
3
選項 53,定義 DHCP 封包類型 3 - DHCPREQUEST

 
選項長度
1
十二月
1

 
選擇權價值
3
十二月
1

 
選項編號
61
十二月
1
客戶端 ID:01(乙太網路)+ 客戶端 MAC 位址

 
選項長度
7
十二月
1

 
選擇權價值
01:2c:ab:25:ff:72:a6
十六進制
7

 
選項編號
60
十二月
 
「供應商類別識別碼」。 就我而言,它會報告 DHCP 用戶端版本。 也許其他設備返回不同的東西。 Windows 例如報表 MSFT 5.0

 
選項長度
11
十二月
 

 
選擇權價值
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
十二月
1
選項 82,重複 DHCPDISCOVER 中的內容

 
選項長度
18
十二月
1

 
選擇權價值
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:歐共體
十二月
18

 
包裝結束
255
十二月
1
255表示資料包結束

DHCP確認

作為從 DHCP 伺服器確認「是的,沒錯,這是您的 IP 位址,我不會將其提供給其他任何人」的確認,從伺服器到客戶端提供 DHCPACK 格式的封包。 它像其他資料包一樣以廣播方式發送。 不過,在下面用 Python 實現的 DHCP 伺服器的程式碼中,為了以防萬一,我透過將封包傳送到特定的客戶端 IP(如果已知)來複製任何廣播請求。 而且,DHCP伺服器根本不關心DHCPACK封包是否已經到達客戶端。 如果客戶端沒有收到 DHCPACK,那麼一段時間後它會簡單地重複 DHCPREQUEST

DHCPACK封包結構表

在包裝中的位置
值的名稱(通用)
例子
主意
位元組
澄清

1
啟動請求
2
十六進制
1
訊息類型。 1 - 客戶端向伺服器發出請求,2 - 伺服器向客戶端發出回應

2
硬件類型
1
十六進制
1
硬體位址類型,在此協定中1 - MAC

3
硬體位址長度
6
十六進制
1
設備MAC位址長度

4
酒花
1
十六進制
1
中間路線數量

5
交易紀錄ID
23:比照:德:1d
十六進制
4
唯一的交易識別碼。 由客戶端在請求操作開始時生成

7
秒過去了
0
十六進制
4
從取得地址過程開始的時間(以秒為單位)

9
引導標誌
8000
十六進制
2
可以設定某些標誌來指示協定參數。 在這種情況下,設定“廣播”

11
客戶端IP位址
0.0.0.0
弦樂
4
客戶端 IP 位址(如果有)

15
您的客戶端IP位址
172.16.134.61
弦樂
4
伺服器提供的 IP 位址(如果可用)

19
下一個伺服器IP位址
0.0.0.0
弦樂
4
伺服器 IP 位址(如果已知)

23
中繼代理IP位址
172.16.114.41
弦樂
4
中繼代理(例如交換器)的IP位址

27
客戶端MAC位址
14:d6:4d:a7:c9:55
十六進制
6
封包發送者(客戶端)的MAC位址

31
用戶端硬體位址填充
 
十六進制
10
預留座位。 通常用零填充

41
伺服器主機名稱
 
弦樂
64
DHCP 伺服器名稱。 通常不傳送

105
啟動檔案名稱
 
弦樂
128
無碟工作站啟動時使用的伺服器上的檔案名

235
魔法餅乾
63:82:53:63
十六進制
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
向 DNS 用戶端提供 DHCP 的選項

 
選項長度
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:歐共體
十二月
18

 
包裝結束
255
十二月
1
255表示資料包結束

安裝

安裝實際上包括安裝工作所需的 python 模組。 假設 MySQL 已經安裝並設定。

FreeBSD的

pkg 安裝 python3 python3 -m Ensurepip pip3 安裝 mysql-connector

Ubuntu

sudo apt-get 安裝 python3 sudo apt-get 安裝 pip3 sudo pip3 安裝 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 本機測試測試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 從使用者中選擇 ip、遮罩、路由器、dns,其中 upper(mac)=upper('{option_3_AgentRemoteId_hex}') 和 upper(port)=upper('{option_1_AgentCircuitId_port_hex}') 從使用者中選擇 ip、遮罩、路由器、dns,其中 upper(mac)=upper('{sw_mac}') 和 upper(port)=upper('{sw_port82}') 從使用者中選擇 ip、遮罩、路由器、dns,其中 upper(mac)=upper('{ClientMacAddress}') 插入歷史記錄(id,dt,mac,ip,comment)值(null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')

現在更詳細地了解標籤:

dhcpserver 部分描述了啟動伺服器的基本設置,即:

  • host - 伺服器在連接埠 67 上偵聽的 IP 位址
  • 廣播 - 哪個 ip 是 DHCPOFFER 和 DHCPACK 的廣播
  • DHCPServer - DHCP 伺服器的 ip 是什麼
  • LeaseTime 下發IP位址的租用時間
  • ThreadLimit - 有多少線程同時運行來處理連接埠 67 上傳入的 UDP 封包。它應該有助於高負載項目😉
  • defaultMask,defaultRouter,defaultDNS - 如果在資料庫中找到 IP,但未為其指定其他參數,則預設為提供給訂閱者的內容

mysql部分:

主機、使用者名稱、密碼、基本名稱——一切都是不言而喻的。 大致的資料庫結構發佈在 GitHub上

查詢部分:接收OFFER/ACK的請求描述如下:

  • Offer_count — 傳回 ip、ma​​sk、router、dns 等結果的請求的行數
  • Offer_n — 查詢字串。 如果return為空,則執行下面的offer請求
  • History_sql - 例如,寫入訂閱者的「授權歷史記錄」的查詢

請求可以包含選項部分中的任何變數或 DHCP 協定中的選項。

選項部分。 這就是事情變得更有趣的地方。 在這裡我們可以建立稍後在查詢部分中使用的變數。

例如:

option_82_hex:sw_port1:20:22

,此命令列採用 DHCP 請求選項 82 中的整行(十六進位格式),範圍為 20 到 22 位元組(含),並將其放入新變數 sw_port1(請求來自的交換器連接埠)中

option_82_hex:sw_mac:26:40

,定義 sw_mac 變量,從範圍 26:40 取得十六進制

您可以透過使用 -d 開關啟動伺服器來查看查詢中可使用的所有可能選項。 我們會看到類似這樣的日誌:

-- 一個DHCPINFORM 資料包到達連接埠67,來自0025224ad764 , b'x91xa5xe0xa3xa5xa9-x8fx8a' , ('172.30.114.25', 68) {'ClientMacAddress'Mac:0025224」 764d ' , ' HType': '乙太網路', '主機名稱': b'x00xa7xe91xa5xa0xa3-x5fx9a', 'ReqListDNS': b'x8xa8xe43xa0.0.0.0xa5.0xa0025224-x764fx172.30.128.13a', 'ReqListDNS': 真, 'ReqListDomainName': True, 'ReqListPerfowmRouterDiscover': True, 'ReqListRoList: True, 'ReqListPerfowmRouterDiscover': True, 'ReqListRo : True, 'ReqListSubnetM 問':True,'ReqListVendorSpecInfo':00,'RequestedIpAddress':'00','供應商':b'MSFT 172.30.114.25','chaddr':'308ad6','dciaddrd' 1' ,'flags':b'x82x12','giaddr':'12','gpoz':53,'hlen':53,'hops':55,'htype':'MAC' 'magic_cookie': b'cx55Sc','op':'DHCPINFORM','選項60':60,'選項61':61,'選項82':82,'選項82':12,'選項01': 06,'選項00':04,' option_00_byte':b'x01x00x06x02x08x00x06x00x1x9x2x82'b'x12010600040001000602080006001x589x2eXx82exb18xad','option_82 12e01eb06ad','選項_00_len':04 00,'option_01_str':“b'x00x06x02x08x00x06x00x1x9x2x768x0.0.0.0x001x589x2eXx”, '結果':假,'秒':1, 'siaddr':'06','sw_mac':'89e8eb3ad','sw_port897':'8','xidbyte':b'

因此,我們可以將任何變數包裝在 {} 中,它將在 SQL 查詢中使用。

讓我們記錄客戶端收到 IP 位址的歷史記錄:

Python中的DHCP+Mysql伺服器

Python中的DHCP+Mysql伺服器

服務器啟動

./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("使用者名稱")[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") 對於dconfig 中的elem:gconfig["broadcast"]=elem.getElementsByTagName("broadcast")[0 ]。firstChild.data gconfig["dhcp_host"]=elem.getElementsByTagName("主機")[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].firstst.data gconfigMask」 "] =elem.getElementsByTagName("defaultMask")[0].firstChild.data gconfig["dhcp_defaultRouter"]=elem.getElementsByTagName("defaultRouter")[0].firstChild.data gconfig["dhcp_defaultDgetNS.elem. (" defaultDNS")[0].firstChild.data qconfig=tree.getElementsByTagName("query") for elem in qconfig: gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data forChild.data forChild.data forChild.data forChild.data forChild.data forChild.data forChild.data forChild.data forChild.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") 對於選項中的elem:node=elem.getElementsByTagName("option") 對於節點中的選項: 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) #將socket切換為廣播模式 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的地方。 事實證明,開箱即用的它可以讓您非常靈活地使用字節碼。 允許它很容易地轉換成十進制值、字串和十六進制——即這就是我們實際需要了解的包的結構。 因此,例如,您可以獲得十六進位位元組範圍並且僅獲取位元組:

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

,將位元組打包到一個結構中:

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

從結構中取得IP:

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

反之亦然:

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

這就是現在的全部內容 😉

來源: www.habr.com

添加評論