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': '0025224ad764', 'ClientMacAddressByte': b'x00 7%"Jx d91d' , ' HType': '以太网', '主机名': b'x5xa0xe3xa5xa9xa8-x8fx43a', 'ReqListDNS': True, 'ReqListDomainName': True, 'ReqListPerfowmRouterDiscover': True, 'ReqListRouter': True, 'ReqListStaticRoute': True, 'ReqListSubnetM 问':True,'ReqListVendorSpecInfo':0.0.0.0,'RequestedIpAddress':'5.0','供应商':b'MSFT 0025224','chaddr':'764ad172.30.128.13','ciaddr':'00' ,'flags':b'x00x172.30.114.25','giaddr':'308','gpoz':6,'hlen':1,'hops':82,'htype':'MAC','magic_cookie': b'cx12Sc','op':'DHCPINFORM','选项12':53,'选项53':55,'选项55':60,'选项60':61,'选项61':82,'选项82':82,' option_12_byte':b'x01x06x00x04x00x01x00x06x02x08x00x06'b'x00x1x9eXx2exb82xad','option_12010600040001000602080006001_hex':'589e2eb82ad','选项_18_len':82 12,'option_01_str':“b'x06x00x04x00x01x00x06x02x08x00x06x00x1x9x2eXx768exb0.0.0.0xad'”,'结果':假,'秒':001, 'siaddr':'589','sw_mac':'2e1eb06ad','sw_port89':'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].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") 对于选项中的 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"])

这就是现在的全部内容 😉

来源: habr.com

添加评论