首先,使用 socket 创建一个IP v4 的 UDP 协议绑定在 67端口,实现最简单的 DHCP 服务端 。
DHCP服务端监听DHCP客户端的广播请求。
import logging
import socket
class SimplestDHCPServer:
def __init__(self):
self.server_socket = None # DHCP 服务端 socket ,负责接受 DHCP 客户端的请求。
self._server_port = 67 # DHCP 服务端口为 67
def start(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# socket.AF_INET --> IPv4 Internet protocols
# socket.SOCK_DGRAM --> 数据报(无连接、不可靠、最大长度固定的消息)
# 参见 《python socket编程1 - socket创建参数说明及参考规范》
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
# socket.SOL_SOCKET --> 将参数作用在 socket 级别上。参见 《python socket编程3 - socket的选项》 level的取值范围
# socket.SO_BROADCAST --> # 设置或获取广播标识。当选择此选项时, 数据报套接字接收向广播地址发送的数据包, 并且可以向广播地址发送数据包。参见 《python socket编程3 - socket的选项》 optName 设置选项的名称
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# socket.SOL_SOCKET --> 将参数作用在 socket 级别上。参见 《python socket编程3 - socket的选项》 level的取值范围
# socket.SO_REUSEADDR --> 表示在一个 bind() 调用中允许重复使用本地地址。参见 《python socket编程3 - socket的选项》 optName 设置选项的名称
self.server_socket.bind(('', self._server_port))
while True:
logging.warning('DHCP 已经启动...')
msg, addr = self.server_socket.recvfrom(8192)
print(msg, addr)
if __name__ == '__main__':
dh = SimplestDHCPServer()
dh.start()
在本地主机运行代码之后,断开网络连接,然后再重新连接网络。输出:
b’\x01\x01\x06\x00\xce\xd6>0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x84\\xf3\xea\xa4\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x03=\x07\x01\x84\\xf3\xea\xa4\x0c2\x04\xc0\xa8\x01k\x0c\rLAPTOP-X1NanoQ\x10\x00\x00\x00LAPTOP-DEVTEA<\x08MSFT 5.07\x0e\x01\x03\x06\x0f\x1f!+,./wy\xf9\xfc\xff’
(‘0.0.0.0’, 68)
索引 | 字段名称 | 长度(字节) | 取值 (对照msg中的值) | 说明 |
---|---|---|---|---|
0 | op | 1 | msg[0] == b’\x01’ | 报文操作类型: 1:请求报文, 2:应答报文 |
1 | htype | 1 | msg[1] == b’\x01’ | 客户端MAC地址类型,指明网络类型: 1:Ethernet MAC |
2 | hlen | 1 | msg[2] == b’\x06’ | 客户端MAC地址长度,以太网MAC地址长度6个字节 |
3 | hops | 1 | msg[3] == b’\x00’ | Client通常设备为0,在中继代理是有意义。 |
4-7 | xid | 4 | Transaction ID,Client选择的随机数。 | |
8-9 | secs | 2 | 由Client填写自Client开始地址获取或续订过程地址以来已经过去了几秒钟。 | |
10-11 | flags | 2 | msg[10:12] == [b’\x00’, b’\x00’] | 目前仅第一个bit使用,broadcast flag。 取1时标识报文为广播报文。当DISCOVER的BOOTP Flag的B-bit未置位,Server应当以单播方式进行回应 OFFER 消息。 |
12-15 | ciaddr | 4 | msg[12:16] == [b’\x00’, b’\x00’, b’\x00’, b’\x00’] | Client IP address。仅当Client处于绑定、续订或重新绑定状态并且可以响应 ARP 请求时才填写. |
16-19 | yiaddr | 4 | msg[16:20] == [b’\x00’, b’\x00’, b’\x00’, b’\x00’] | 也即DHCP Server告知Client分配到的地址。 |
20-23 | siaddr | 4 | msg[20:24] == [b’\x00’, b’\x00’, b’\x00’, b’\x00’] | bootstrap引导所使用的下一个服务器的地址。在DHCPOFFER, DHCPACK阶段携带。 |
24-27 | giaddr | 4 | Relay agent IP address, 只在中继存在的场景下使用。 | |
27-43 | chaddr | 16 | Client hardware address. | |
44-107 | sname | 64 | Optional server host name, null terminated string. | |
108-235 | file | 128 | Boot file name, null terminated string; “generic” name or null in DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER. | |
236- | options | 可变长 | msg[236:240] == [b’\x63’, b’\x82’, b’\x53’, b’\x63’] | DHCP Option前4个字节为magic cookie。 通常固定为0x63,0x82,0x53和0x63。 |
编号 | 名字 | 长度 | 说明 |
---|---|---|---|
0 | Pad | 0 | None |
1 | Subnet Mask | 4 | Subnet Mask Value |
2 | Time Offset | 4 | Time Offset in Seconds from UTC (note: deprecated by 100 and 101) |
3 | Router | N | N/4 Router addresses |
4 | Time Server | N | N/4 Timeserver addresses |
5 | Name Server | N | N/4 IEN-116 Server addresses |
6 | Domain Server | N | N/4 DNS Server addresses |
7 | Log Server | N | N/4 Logging Server addresses |
8 | Quotes Server | N | N/4 Quotes Server addresses |
9 | LPR Server | N | N/4 Printer Server addresses |
10 | Impress Server | N | N/4 Impress Server addresses |
11 | RLP Server | N | N/4 RLP Server addresses |
12 | Hostname | N | Hostname string |
13 | Boot File Size | 2 | Size of boot file in 512 byte chunks |
14 | Merit Dump File | N | Client to dump and name the file to dump it to |
15 | Domain Name | N | The DNS domain name of the client |
16 | Swap Server | N | Swap Server address |
17 | Root Path | N | Path name for root disk |
18 | Extension File | N | Path name for more BOOTP info |
19 | Forward On/Off | 1 | Enable/Disable IP Forwarding |
20 | SrcRte On/Off | 1 | Enable/Disable Source Routing |
21 | Policy Filter | N | Routing Policy Filters |
22 | Max DG Assembly | 2 | Max Datagram Reassembly Size |
23 | Default IP TTL | 1 | Default IP Time to Live |
24 | MTU Timeout | 4 | Path MTU Aging Timeout |
25 | MTU Plateau | N | Path MTU Plateau Table |
26 | MTU Interface | 2 | Interface MTU Size |
27 | MTU Subnet | 1 | All Subnets are Local |
28 | Broadcast Address | 4 | Broadcast Address |
29 | Mask Discovery | 1 | Perform Mask Discovery |
30 | Mask Supplier | 1 | Provide Mask to Others |
31 | Router Discovery | 1 | Perform Router Discovery |
32 | Router Request | 4 | Router Solicitation Address |
33 | Static Route | N | Static Routing Table |
34 | Trailers | 1 | Trailer Encapsulation |
35 | ARP Timeout | 4 | ARP Cache Timeout |
36 | Ethernet | 1 | Ethernet Encapsulation |
37 | Default TCP TTL | 1 | Default TCP Time to Live |
38 | Keepalive Time | 4 | TCP Keepalive Interval |
39 | Keepalive Data | 1 | TCP Keepalive Garbage |
40 | NIS Domain | N | NIS Domain Name |
41 | NIS Servers | N | NIS Server Addresses |
42 | NTP Servers | N | NTP Server Addresses |
43 | Vendor Specific | N | Vendor Specific Information |
44 | NETBIOS Name Srv | N | NETBIOS Name Servers |
45 | NETBIOS Dist Srv | N | NETBIOS Datagram Distribution |
46 | NETBIOS Node Type | 1 | NETBIOS Node Type |
47 | NETBIOS Scope | N | NETBIOS Scope |
48 | X Window Font | N | X Window Font Server |
49 | X Window Manager | N | X Window Display Manager |
50 | Address Request | 4 | Requested IP Address,已经得到地址后,确认地址 |
51 | Address Time | 4 | IP Address Lease Time |
52 | Overload | 1 | Overload “sname” or “file” |
53 | DHCP Msg Type | 1 | DHCP Message Type,DHCP请求消息的类型, 这个值对比下面的53号类型表说明,可以知道消息的具体类型 |
54 | DHCP Server Id | 4 | DHCP Server Identification,确认接受分配的DHCP标识 |
55 | Parameter List | N | Parameter Request List |
56 | DHCP Message | N | DHCP Error Message |
57 | DHCP Max Msg Size | 2 | DHCP Maximum Message Size |
58 | Renewal Time | 4 | DHCP Renewal (T1) Time |
59 | Rebinding Time | 4 | DHCP Rebinding (T2) Time |
60 | Class Id | N | Class Identifier |
61 | Client Id | N | Client Identifier |
… | |||
222-223 | Unassigned | ||
224-254 | Reserved (Private Use) | ||
255 | End | 0 | None |
更多选项见文章底部参考网址。
Value | Message Type |
---|---|
1 | DHCPDISCOVER |
2 | DHCPOFFER |
3 | DHCPREQUEST |
4 | DHCPDECLINE |
5 | DHCPACK |
6 | DHCPNAK |
7 | DHCPRELEASE |
8 | DHCPINFORM |
9 | DHCPFORCERENEW |
10 | DHCPLEASEQUERY |
11 | DHCPLEASEUNASSIGNED |
12 | DHCPLEASEUNKNOWN |
13 | DHCPLEASEACTIVE |
14 | DHCPBULKLEASEQUERY |
15 | DHCPLEASEQUERYDONE |
16 | DHCPACTIVELEASEQUERY |
17 | DHCPLEASEQUERYSTATUS |
18 | DHCPTLS |
import logging
import socket
class SimplestDHCPServer:
def __init__(self):
self.server_socket = None
self._server_port = 67
def start(self):
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.server_socket.bind(('', self._server_port))
while True:
logging.warning('DHCP 已经启动...')
msg, addr = self.server_socket.recvfrom(8192)
print(msg, addr)
#以下为新增代码
print(len(msg)) # 请求总长度
print(hex(msg[0])) # 头部主体,索引 0 -- 报文操作类型: 1
print(hex(msg[1])) # 头部主体,索引 1 -- 客户端MAC地址类型: 1
print(hex(msg[2])) # 头部主体,索引 2 -- 客户端MAC地址长度: 6
print(hex(msg[3])) # 头部主体,索引 3 -- 是否启用代理, Client设备通常为:0
print(hex(msg[10])) # 头部主体,索引 10~11 -- 是否为广播报文,取1时标识报文为广播报文.
print(hex(msg[11])) # 头部主体,索引 10~11 -- 是否为广播报文,取1时标识报文为广播报文.
print(hex(msg[12])) #头部主体,索引 12~15 -- 客户端 IP 地址
print(hex(msg[13])) #头部主体,索引 12~15 -- 客户端 IP 地址
print(hex(msg[14])) #头部主体,索引 12~15 -- 客户端 IP 地址
print(hex(msg[15])) #头部主体,索引 12~15 -- 客户端 IP 地址
print(hex(msg[16])) #头部主体,索引 16~19 -- 客户端确认使用的 IP 地址
print(hex(msg[17])) #头部主体,索引 16~19 -- 客户端确认使用的 IP 地址
print(hex(msg[18])) #头部主体,索引 16~19 -- 客户端确认使用的 IP 地址
print(hex(msg[19])) #头部主体,索引 16~19 -- 客户端确认使用的 IP 地址
print(hex(msg[20])) #头部主体,索引 20~23 -- 使用的DHCP服务器地址
print(hex(msg[21])) #头部主体,索引 20~23 -- 使用的DHCP服务器地址
print(hex(msg[22])) #头部主体,索引 20~23 -- 使用的DHCP服务器地址
print(hex(msg[23])) #头部主体,索引 20~23 -- 使用的DHCP服务器地址
print(hex(msg[236])) #头部主体,索引 236~239 -- DHCP Option的开始标识
print(hex(msg[237])) #头部主体,索引 236~239 -- DHCP Option的开始标识
print(hex(msg[238])) #头部主体,索引 236~239 -- DHCP Option的开始标识
print(hex(msg[239])) #头部主体,索引 236~239 -- DHCP Option的开始标识
print('以上是头部主体信息')
print('以下是头部option信息')
print('option code:', hex(msg[240]))
print('length:', hex(msg[241]))
print('value:', hex(msg[242]))
if msg[242] == 0x3:
print("这是一个DHCP Request")
if __name__ == '__main__':
dh = SimplestDHCPServer()
dh.start()
322
#请求总长度
0x1#头部主体,索引 0
– 报文操作类型:1
0x1#头部主体,索引 1
– 客户端MAC地址类型:1
0x6#头部主体,索引 2
– 客户端MAC地址长度: 6
0x0#头部主体,索引 3
– 是否启用代理, Client设备通常为:0
0x0#头部主体,索引 10~11
– 是否为广播报文,取1时标识报文为广播报文.
0x0#头部主体,索引 10~11
– 是否为广播报文,取1时标识报文为广播报文.
0x0#头部主体,索引 12~15
– 客户端 IP 地址
0x0#头部主体,索引 12~15
– 客户端 IP 地址
0x0#头部主体,索引 12~15
– 客户端 IP 地址
0x0#头部主体,索引 12~15
– 客户端 IP 地址
0x0#头部主体,索引 16~19
– 客户端确认使用的 IP 地址
0x0#头部主体,索引 16~19
– 客户端确认使用的 IP 地址
0x0#头部主体,索引 16~19
– 客户端确认使用的 IP 地址
0x0#头部主体,索引 16~19
– 客户端确认使用的 IP 地址
0x0#头部主体,索引 20~23
– 使用的DHCP服务器地址
0x0#头部主体,索引 20~23
– 使用的DHCP服务器地址
0x0#头部主体,索引 20~23
– 使用的DHCP服务器地址
0x0#头部主体,索引 20~23
– 使用的DHCP服务器地址
0x63#头部主体,索引 236~239
– DHCP Option的开始标识
0x82#头部主体,索引 236~239
– DHCP Option的开始标识
0x53#头部主体,索引 236~239
– DHCP Option的开始标识
0x63#头部主体,索引 236~239
– DHCP Option的开始标识
以上是头部主体信息
以下是头部option信息
option code: 0x35#option code,索引 240
– 1个字节, 0x35 ( 53) 标识 DHCP消息类型
length: 0x1#长度,索引 241
– 1 个字节,0x1
value: 0x3#取值 ,索引 242
– 1个字节 , 0x3 (3) 标识是 DHCP客户端发出的请求IP的消息。
这是一个DHCP Request
DHCP 分为四个阶段: Discovery、Offer、Request、Afirmledgement(也称为 DORA 进程)。DHCP 使用这个过程为客户端提供 IP 地址。
Npcap is the Nmap Project's packet capture (and sending) library for Microsoft Windows.
It implements the open Pcap API using a custom Windows kernel driver alongside our Windows build of the excellent libpcap library.
This allows Windows software to capture raw network traffic (including wireless networks, wired ethernet, localhost traffic, and many VPNs) using a simple, portable API.
Npcap allows for sending raw packets as well. Mac and Linux systems already include the Pcap API...
参考资料: