WinDHCPD 教程:Windows 原生 DHCP 服务器(Python + Scapy)

局域网里没有 DHCP 服务器,几十台电脑就得一台一台手工配 IP;换了网段、加了新设备,改配置能改到手软。WinDHCPD 就是为了解决这个问题的:一个基于 Python 3 + Scapy 的轻量级 DHCP 服务器,跑在 Windows 上,不用装 Windows Server,不用配复杂的 DHCP 角色,改一个 YAML 配置文件就能给整个网段自动分配 IP,还自带 Web 管理面板看租约和统计。本教程从前置条件、安装、配置讲到跨网段 Relay 和故障排查,照着做就能跑起来。

一、项目简介

WinDHCPD 是 AI-Agent 组织下的开源项目(Python,master 分支),在 Windows 上以管理员权限运行,用 Scapy 捕获和发送 DHCP 数据包,用 Flask 提供 Web 管理面板。它的定位很明确:轻量、可移植、够用——不是要替代 Windows Server DHCP 或 ISC DHCP,而是给实验室、临时组网、小型办公网、装机现场这些场景提供一个随开随用的 DHCP 服务。

整个项目结构非常简单,只有十几个文件,核心逻辑集中在 dhcpd/ 目录:

win-dhcpd/
├── run.py                # 入口脚本
├── dhcpd.yaml.example    # 配置文件示例
├── requirements.txt      # Python 依赖
├── dhcpd/
│   ├── config.py         # 配置加载与校验
│   ├── lease.py          # 租约数据库(内存 + JSON 持久化)
│   ├── parser.py         # DHCP 包解析与构建(Scapy)
│   ├── server.py         # DHCP 服务器主循环(Sniffer + 响应)
│   └── web/
│       ├── app.py        # Flask Web 面板
│       └── templates/
│           └── index.html # 管理界面(深色主题,自动刷新)

注意:WinDHCPD 只处理 IPv4 DHCP,不支持 IPv6 和 DHCPv6。如果网络需要 IPv6 地址分配,请配置 SLAAC 或单独部署 DHCPv6 服务器。

二、功能特性

功能说明
完整 DORA 握手DISCOVER → OFFER → REQUEST → ACK,符合 RFC 2131
多作用域支持多个网段(/24),可通过 DHCP Relay 跨网段服务
静态绑定MAC → IP 固定分配,优先于动态分配
租约管理内存索引 + JSON 持久化,支持释放、续租、清理过期
Web 管理面板Flask 面板,实时查看租约、作用域、统计(端口 8080)
NACK 拒绝对不合法请求返回 NACK
日志记录控制台 + 文件双输出

其中最值得说的是两点。第一是多作用域 + Relay:一个 WinDHCPD 实例可以同时服务多个网段,配合路由器的 DHCP Relay(ip helper-address),跨网段的客户端也能拿到地址。第二是静态绑定优先:打印机、服务器、NAS 这类需要固定 IP 的设备,用 MAC 绑定写死,动态池再怎么分配也不会抢走它的地址。

三、适用场景

  • 实验室与测试环境:临时搭一个网段做测试,几分钟就能让所有测试机自动获取 IP,用完即停。
  • 装机与 PXE 批量部署现场:配合 PXE 引导做批量装机,DHCP 是前置条件,WinDHCPD 比现搭一套 Windows Server DHCP 快得多。
  • 小型办公网 / 门店网络:没有专业网管设备时,一台常开的 Windows 电脑就能承担 DHCP 职责。
  • 教学与协议学习:代码量小、结构清晰,读 parser.py 和 server.py 就能理解 DHCP 协议的完整交互过程。
  • 多网段分支机构:总部一台 WinDHCPD,通过各分支路由器的 Relay 统一给多个网段分配地址。

不适用场景也要说清楚:大型生产网络、需要高可用(主备切换)的环境、需要与 AD 域深度集成的场景,应该用 Windows Server DHCP 或 ISC DHCP。WinDHCPD 是单进程服务,没有高可用机制,机器一挂,整个网段的客户端就拿不到新地址(已拿到租约的客户端在租期内不受影响)。

四、工作原理:DORA 四步握手

DHCP 分配地址的过程叫 DORA,是四个报文的往返。理解这个流程,排障时才知道该看哪一步。

步骤报文方向作用
1. DiscoverDHCPDISCOVER客户端 → 广播客户端上线,大喊“有没有 DHCP 服务器?”
2. OfferDHCPOFFER服务器 → 广播WinDHCPD 从地址池挑一个空闲 IP 发过去
3. RequestDHCPREQUEST客户端 → 广播客户端确认“我就要这个 IP”
4. AckDHCPACK服务器 → 广播WinDHCPD 确认分配,写入租约数据库
客户端                    服务器
  │                        │
  │  DHCPDISCOVER (广播)   │
  │ ─────────────────────► │
  │                        │ 分配 IP → 发 OFFER
  │  DHCPOFFER (广播)      │
  │ ◄────────────────────── │
  │                        │
  │  DHCPREQUEST (广播)    │
  │ ─────────────────────► │
  │                        │ 更新租约 → 发 ACK
  │  DHCPACK (广播)        │
  │ ◄────────────────────── │
  │                        │
  │  DHCPRELEASE           │
  │ ─────────────────────► │ 释放 IP

租约到期前客户端会主动续租(REQUEST),WinDHCPD 更新租约即可;客户端关机或执行 ipconfig /release 时会发 RELEASE,服务器把地址放回地址池。租约过期后也会被清理,地址重新进入可分配状态。

五、前置条件:Python 与 Npcap

跑 WinDHCPD 之前要装好两样东西,缺一不可。

5.1 安装 Python 3.8+

Windows 上安装 Python 3.8 或更高版本,安装时勾选 Add Python to PATH,装完验证:

python --version
# Python 3.11.x

pip --version

5.2 安装 Npcap(Scapy 的依赖)

Scapy 在 Windows 上需要 Npcap 来捕获和发送网络数据包。下载地址是 https://nmap.org/npcap/,安装时必须勾选 "Install Npcap in WinPcap API-compatible Mode",否则 Scapy 找不到发包接口。其他选项默认安装即可。

5.3 安装 Python 依赖

pip install -r requirements.txt

依赖只有三个,作用各不相同:

  • scapy(≥2.5):网络数据包捕获与发送,DHCP 报文的解析与构建都靠它。
  • flask(≥3.0):Web 管理面板。
  • pyyaml(≥6.0):配置文件解析。

六、安装与快速开始

6.1 创建配置文件

copy dhcpd.yaml.example dhcpd.yaml

编辑 dhcpd.yaml,把作用域(网段、地址池、网关、DNS)改成实际网络参数。配置格式见第七节。

6.2 以管理员身份运行

必须使用管理员权限,因为捕获 UDP 67 端口的广播数据包需要特权。右键命令提示符选择“以管理员身份运行”,再执行:

# 默认启动(使用 dhcpd.yaml)
python run.py

# 指定配置文件
python run.py dhcpd.yaml

# 指定监听网卡(多网卡机器必备)
python run.py dhcpd.yaml -i "以太网"

# 禁用 Web 面板
python run.py dhcpd.yaml --no-web

# 指定 Web 端口
python run.py dhcpd.yaml --web-port 9000

6.3 访问 Web 面板

服务启动后,打开浏览器访问 http://localhost:8080,就能看到租约、作用域和统计信息。面板是深色主题、自动刷新,不用手动点刷新就能看到新上线的客户端。

6.4 客户端验证

# 客户端先释放旧地址,再重新获取
ipconfig /release
ipconfig /renew

# 查看拿到的地址、网关、DNS 和租期
ipconfig /all

七、配置文件详解

一份完整的 dhcpd.yaml 如下,覆盖了服务器、作用域、静态绑定和日志的全部配置项:

# ── 服务器设置 ──
server:
  ip: "192.168.1.1"          # DHCP 服务器 IP(响应包中的 server_id)

# ── 作用域 ──
scopes:
  - name: "Office-LAN"       # 作用域名称
    network: "192.168.1.0/24"
    start_ip: "192.168.1.100"
    end_ip: "192.168.1.200"
    subnet_mask: "255.255.255.0"
    router: "192.168.1.1"
    dns_servers:
      - "8.8.8.8"
      - "114.114.114.114"
    lease_seconds: 86400     # 租期 24 小时
    domain: "local"
    enable: true

# ── 静态绑定 ──
bindings:
  - mac: "aa:bb:cc:dd:ee:01"
    ip: "192.168.1.10"
    scope: "Office-LAN"
    hostname: "printer"

# ── 其他 ──
log_file: "dhcpd.log"
log_level: "INFO"            # DEBUG / INFO / WARNING / ERROR
web_host: "0.0.0.0"
web_port: 8080
# interface: "以太网"        # 可选:指定监听网卡
参数说明示例
server.ipDHCP 服务器自身 IP192.168.1.1
scopes[].network网段(CIDR)192.168.1.0/24
scopes[].start_ip地址池起始192.168.1.100
scopes[].end_ip地址池结束192.168.1.200
scopes[].router默认网关192.168.1.1
scopes[].dns_serversDNS 服务器列表[8.8.8.8, 114.114.114.114]
scopes[].lease_seconds租期(秒)86400(24 小时)
bindings[].mac静态绑定 MACaa:bb:cc:dd:ee:01
bindings[].ip静态绑定 IP192.168.1.10

几个配置要点:地址池不要和静态绑定的 IP 重叠,否则动态分配可能把绑定地址发出去;租期按场景调,流动性大的临时网络可以设短一点(如 3600 秒),固定设备多的办公网设 86400 秒;多网卡机器一定要用 interface 或 -i 指定监听网卡,否则 Scapy 可能监听到错误的网卡上,客户端收不到 OFFER。

八、Web 管理面板

Web 面板默认监听 8080 端口(web_host 默认 0.0.0.0,局域网内其他机器也能访问;只想本机访问可以改成 127.0.0.1)。面板提供三类实时信息:

  • 租约列表:当前已分配的 IP、对应 MAC、主机名、租期到期时间。
  • 作用域:每个作用域的网段、地址池范围、已用/可用数量。
  • 统计:收到的 DISCOVER / REQUEST 数量、分配成功与失败计数。

面板是深色主题、自动刷新,适合挂在第二块屏幕上实时观察。如果不需要面板(比如纯后台运行),启动时加 --no-web 即可;想换端口用 --web-port 9000。

九、网络拓扑:单网段与 DHCP Relay

9.1 场景一:单机 DHCP 服务器

客户端 ────── 交换机/集线器 ────── WinDHCPD (192.168.1.1)
   ↑                              ↑
   │  广播 DISCOVER                │  响应 OFFER/ACK
   └────────────────────────────────┘

WinDHCPD 所在的网卡必须是混杂模式或连接在 Hub / 支持组播的交换机上,并且同一网段只能有一个 DHCP 服务器,Windows 自带的 DHCP Server 服务必须关闭,否则两个服务器抢答,客户端会随机拿到其中一方的地址。

9.2 场景二:跨网段(DHCP Relay)

子网 A 客户端 ── 路由器(Relay) ── WinDHCPD (192.168.1.1)
                            ↑
                      giaddr = 10.0.2.1

跨网段时,DHCP 广播到不了服务器,需要路由器做 DHCP Relay:路由器收到客户端的广播后,把它单播转发给 WinDHCPD,并在报文的 giaddr 字段填上客户端所在网段的网关地址(例如 10.0.2.1)。WinDHCPD 根据 giaddr 判断客户端属于哪个网段,再从对应的作用域里分配地址。路由器上的配置以 Cisco 为例:

# 在客户端所在网段的网关接口上配置
interface GigabitEthernet0/0
 ip helper-address 192.168.1.1    # WinDHCPD 的 IP

多作用域的配置方法就是在 scopes 里再加一个网段,network、start_ip、end_ip、router 按新网段填写即可,WinDHCPD 会根据 giaddr 自动选择。

十、常见问题与排查

10.1 客户端获取不到 IP

  • 必须以管理员身份运行:UDP 67 端口需要特权,普通权限启动会收不到广播。
  • 检查 Npcap 是否安装,且安装时勾选了 WinPcap 兼容模式。
  • 确认网卡选择:多网卡机器用 -i "网卡名" 指定正确的网卡。
  • 同一网段不能有另一个 DHCP 服务器:Windows 自带的 DHCP Server 服务必须关闭。

10.2 如何查看日志

# 实时查看
type dhcpd.log

# 排障时把日志级别改成 DEBUG(配置文件中)
log_level: "DEBUG"

日志是控制台 + 文件双输出,DEBUG 级别会记录每个收发报文的细节,适合定位“客户端发了 DISCOVER 但服务器没响应”这类问题。

10.3 支持 IPv6 吗?支持 DHCPv6 吗?

都不支持。本服务器仅处理 IPv4 DHCP。如需 IPv6 地址分配,请配置 SLAAC 或单独部署 DHCPv6 服务器。

10.4 停止服务器

  • 控制台:按 Ctrl+C。
  • 任务管理器:结束 python.exe 进程。

租约数据自动保存到 leases.db,下次启动时自动加载,已经分配出去的租约不会因为重启而丢失。

十一、安全注意事项与运维建议

DHCP 服务的破坏力比看起来大:一个配错的 DHCP 服务器能让整个网段断网。所以部署前请牢记以下几点:

  • 不要在生产网络中直接部署,除非你完全了解后果。先用隔离网络测试,确认无误后再部署。
  • 单点故障:这台机器挂了,所有 DHCP 客户端都无法获取新 IP。重要网络请考虑备用方案。
  • 冲突风险:同一网段有两个 DHCP 服务器会导致客户端随机获取 IP,网关、DNS 可能被发错。
  • 端口监听:服务器会监听 UDP 67 的广播包,Web 面板默认监听 0.0.0.0:8080,内网可达,不需要对外暴露时请把 web_host 改成 127.0.0.1 或用防火墙限制访问来源。
  • 定期备份配置和租约:dhcpd.yaml 和 leases.db 是恢复服务的关键文件,定期备份。
  • 日志轮转:长期运行时注意 dhcpd.log 的大小,定期清理或轮转。

十二、总结

WinDHCPD 用不到二十个文件就实现了一个完整可用的 DHCP 服务器:RFC 2131 的 DORA 握手、多作用域、静态绑定、租约持久化、Web 面板,一样不少。它的优势是轻——Python + Scapy,不用装 Windows Server 角色,改个 YAML 就能跑;代价是没有高可用和 AD 集成,适合实验室、装机现场、小型办公网这些“够用就好”的场景。按本文的步骤:装 Python 和 Npcap → 复制配置文件 → 管理员身份运行 → 客户端 ipconfig /renew 验证,十分钟就能让一个网段自动分配起地址来。

发表回复

后才能评论