該項目的目的是:
- 了解 IPv4 網路上的 DHCP
- 學習Python(比從頭開始多一點😉)
- 伺服器更換
DB2DHCP (我的叉子),原創這裡 ,為新作業系統組裝變得越來越困難。 我不喜歡它是一個二進位文件,無法“立即更改” - 取得工作的 DHCP 伺服器,該伺服器能夠使用訂戶的 MAC 或交換器 MAC+連接埠組合選擇訂戶的 IP 位址(選項 82)
- 寫另一輛自行車(喔!這是我最喜歡的活動)
- 在 Habrahabr 上收到有關您的俱樂部慣用手的評論(或者更好的是,邀請)😉
結果:它有效 😉 在 FreeBSD 和 Ubuntu 作業系統上進行了測試。 理論上,可以要求程式碼在任何作業系統下工作,因為程式碼中似乎沒有具體的綁定。
小心! 還有很多事情要做。
連結到業餘愛好者的儲存庫
安裝、設定和使用「研究硬體」結果的過程就低了很多,然後再講一點關於DHCP協定的理論。 為了我自己。 對於歷史😉
一點理論
什麼是 DHCP
這是一種網路協議,允許設備從 DHCP 伺服器查找其 IP 位址(以及網關、DNS 等其他參數)。 資料包使用 UDP 協定進行交換。 設備請求網路參數時的整體工作原理如下:
- 設備(客戶端)在整個網路中發送 UDP 廣播請求 (DHCPDISCOVER),請求「好吧,有人給我一個 IP 位址」。 此外,通常(但並非總是)請求從連接埠 68(來源)發出,目的地是連接埠 67(目的地)。 有些設備也會從連接埠 67 發送資料包。 用戶端設備的 MAC 位址包含在 DHCPDISCOVER 封包內。
- 位於網路上的所有 DHCP 伺服器(可能有多個)都會形成一個 DHCPOFFER 報價,其中包含發送 DHCPDISCOVER 的設備的網路設置,並透過網路廣播它。 根據先前在 DHCPDISCOVER 請求中提供的用戶端 MAC 位址來識別此封包的目標使用者。
- 用戶端接受帶有網路設定建議的資料包,選擇最有吸引力的一個(標準可能不同,例如資料包傳遞的時間、中間路由的數量),並使用網路設定發出「正式請求」DHCPREQUEST從牠喜歡的DHCP 伺服器。 在這種情況下,封包將會傳送到特定的 DHCP 伺服器。
- 收到 DHCPREQUEST 的伺服器發送 DHCPACK 格式的資料包,其中再次列出適用於該客戶端的網路設置
此外,還有來自客戶端的 DHCPINFORM 封包,其目的是通知 DHCP 伺服器「客戶端處於活動狀態」並且正在使用已發佈的網路設定。 在此伺服器的實作中,這些資料包將被忽略。
封裝格式
一般來說,乙太網路資料包框架看起來像這樣:
在我們的例子中,我們將只考慮直接來自 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部分:
主機、使用者名稱、密碼、基本名稱——一切都是不言而喻的。 大致的資料庫結構發佈在
查詢部分:接收OFFER/ACK的請求描述如下:
- Offer_count — 傳回 ip、mask、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 位址的歷史記錄:
服務器啟動
./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