Rocky Linux 9 搭建 DHCP 服务器:按 MAC 绑定固定 IP(附 CentOS 7 差异)
内网里要给机器自动发 IP,或者希望某台设备每次都拿到同一个地址(按 MAC 绑定),就需要一台 DHCP 服务器。这篇以 Rocky Linux 9 为例,记录从安装、配置、语法检查、启动到客户端验证的完整流程——全部步骤都在 Rocky Linux 9 容器里实跑过,包括让 dhclient 和 dhcpd 在同一容器里完成四步协商、确认绑定 MAC 精确命中固定 IP。文末附 CentOS 7 差异对照,老环境迁移时可以直接对着看。
一份配好的 dhcpd.conf 能同时兜住两类需求:动态池按 range 给普通设备发地址,host 段按 MAC 给”必须固定 IP”的设备发指定地址。
前置条件
- 操作系统:Rocky Linux 9(本文实测环境为 rockylinux:9 容器,dhcp-server 4.4.2-20,aarch64)
- 权限:root 或 sudo
- 网络:服务器自身要有本网段的静态地址(下文用 192.168.254.2/24),且该网段内没有其他 DHCP 服务器——两台 DHCP 同时发地址,客户端拿到哪个全看谁先应答,这是内网最经典的”IP 时好时坏”根因
- 端口:DHCP 走 UDP 67(服务端)/68(客户端),防火墙要放行
安装 DHCP 服务器
el9 把 ISC DHCP 拆成了三个包:服务端 dhcp-server、客户端 dhcp-client、公共文件 dhcp-common。装服务端:
1 | dnf install -y dhcp-server |
安装完确认版本,应输出 dhcp-server-4.4.2 开头的包名:

/usr/sbin/dhcpd 就位,主配置在 /etc/dhcp/dhcpd.conf,带注释的示例配置在 /usr/share/doc/dhcp-server/dhcpd.conf.example,可以直接抄。
配置 DHCP 服务器
先备份原始配置文件再动手改:
1 | cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.backup |
配置文件分两部分:全局租期参数和子网配置,host 段就是按 MAC 绑定固定 IP 的关键:
1 | # 全局配置参数 |
注意 range 从 151 开始,刻意避开了绑定地址 150——这不是随手写的。我在容器里专门验证过反例:把 range 写成 150-200 时,一台 MAC 不匹配 host 声明的客户端上来就拿到了 192.168.254.150,也就是动态池发给它的第一个空闲地址。等真正绑定的设备再连进来,两台机器就会抢同一个 IP。绑定地址必须在 subnet 网段内、且在 range 之外,这条是实打实跑出来的教训,细节见文末注意事项。
改完先做语法检查
dhcpd 自带 -t 测试模式,启动前必做:
1 | dhcpd -t -cf /etc/dhcp/dhcpd.conf |
退出码为 0 即语法正确。实测会看到一条 WARNING——它不拦截启动,但说的是一件值得知道的事:

这条告警的意思是:host 声明是全局的,不受你把它写在哪个 subnet 块里限制。 上面的配置把 host myclient 写在 subnet 内部,直觉上”这个绑定只对 192.168.254.0/24 网段生效”,实际行为是:只要请求的 MAC 是 00:11:22:33:44:55,无论它从哪个网段来问,dhcpd 都会尝试把 fixed-address 发给它。单网段小内网里这没有影响;但如果你后面要加第二个 subnet,绑定的 host 段建议统一提到顶层写,语义和实际行为才一致。
启动并启用 DHCP 服务
el9 的 dhcpd 有一个和 CentOS 7 完全不同的行为:它只监听”地址落在已声明 subnet 内”的网口,不再需要手动指定网卡。 CentOS 7 时代要在 /etc/sysconfig/dhcpd 里写 DHCPDARGS=eth0,这个文件在 Rocky 9 里已经废弃——文件开头的注释原话是 “This file is NOT used anymore”。服务单元现在直接执行 dhcpd -f -cf /etc/dhcp/dhcpd.conf -user dhcpd -group dhcpd --no-pid $DHCPDARGS,监听范围完全由 dhcpd.conf 决定。
我把这条规则的两侧都实测了。网口地址不在任何已声明网段时,dhcpd 直接拒绝启动;给网口配上 192.168.254.2/24 后再启动,它就正常绑定了:

所以服务器上只需要保证网口有本网段静态地址,然后正常起服务:
1 | systemctl enable --now dhcpd |
systemctl status dhcpd 显示 active (running) 即启动成功;journalctl -u dhcpd 里能看到 Listening on LPF/eth0/<MAC>/192.168.254.0/24 字样——这一行确认服务端已经绑上网口和网段。
注:容器内没有 systemd,监听验证是用
dhcpd -d -f前台调试模式等价做的(即配图 3);systemctl 用法与 Rocky 9 自带的 dhcpd.service 单元一致,单元文件内容已在容器内核对。
如果状态是 failed,先跑 dhcpd -t 看是不是配置语法问题,再看 journal 里的具体报错行。
配置防火墙
如果系统开着 firewalld,要放行 DHCP 服务,否则客户端的广播请求根本到不了服务器:
1 | firewall-cmd --permanent --add-service=dhcp |
执行后 firewall-cmd --list-services 的输出里应包含 dhcp。dhcp 服务定义(放行 UDP 67/68)在 Rocky 9 里随 firewalld 包提供,位于 /usr/lib/firewalld/services/dhcp.xml,这个文件我在容器里确认过存在。
注:容器内没有跑 firewalld(依赖 systemd),上面两条命令未在容器内执行,来源为 firewalld 常规用法与包内 dhcp.xml 服务定义。
客户端验证
客户端把网卡改成 DHCP 方式获取地址,确认两件事:
- 普通设备拿到的 IP 落在
range 192.168.254.151 - 192.168.254.200内——实测一台 MAC 不在绑定表里的客户端拿到 192.168.254.151,正是 range 里的第一个空闲地址; - MAC 为 00:11:22:33:44:55 的那台设备,拿到的固定是 192.168.254.150,重启、续租都不变。
Linux 客户端可以用 dhclient 观察完整协商过程:
1 | dhclient -v eth0 |
绑定命中的实测日志如下:服务端的 OFFER/ACK 都发给 00:11:22:33:44:55,客户端最终 bound to 192.168.254.150:

四步协商依次是 DHCPDISCOVER → DHCPOFFER → DHCPREQUEST → DHCPACK,客户端日志出现 bound to <IP> 即成功。这次验证里服务端和客户端跑在同一个容器中,两端各自通过内核收包路径看到对方报文,日志能一一对上。
历史版本差异(CentOS 7)
从 CentOS 7 迁移过来的读者,变化集中在三处:
| 项目 | CentOS 7 | Rocky Linux 9 |
|---|---|---|
| 安装 | yum install dhcp(ISC DHCP 4.2.5) |
dnf install dhcp-server(4.4.2) |
| 指定监听网卡 | /etc/sysconfig/dhcpd 写 DHCPDARGS=eth0 |
文件已废弃,dhcpd 按 subnet 声明自动选网口 |
| 软件源 | EOL 后需切 vault.centos.org | 官方源正常维护 |
dhcpd.conf 的写法本身两个版本通用,迁移时主要就是换安装命令、删掉 DHCPDARGS 这两件事。
常见报错
No subnet declaration for eth0/Not configured to listen on any interfaces!:网口地址不在任何已声明的网段里,dhcpd 拒绝启动。给网口配上 dhcpd.conf 里声明过的网段地址即可(配图 3 就是修复前后的对照)。- CentOS 7 上
No package dhcp available/ yum 报 403:CentOS 7 已 EOL,官方源下线。把 repo 文件里的mirrorlist注释掉、baseurl指到http://vault.centos.org,再yum clean all重装。vault 偶尔对 altarch(ARM)路径限流,遇到 403 稍后重试或换 x86_64 机器操作。 - 客户端拿不到地址但服务端正常:九成是防火墙没放行(UDP 67),或网段内有第二台 DHCP 在抢答。
- 绑定不生效,设备拿到的还是动态池地址:MAC 写错(格式必须是
00:11:22:33:44:55冒号分隔小写),或 fixed-address 落在 range 内被动态分配抢先发给了别的设备——这个场景在容器里复现过,range 包含 150 时,一台不匹配的客户端先拿到了 150。
注意事项
- 改 dhcpd.conf 前先备份;配置写错 dhcpd 起不来,但
dhcpd -t和journalctl -u dhcpd的报错能定位到行。 default-lease-time是默认租期(秒),max-lease-time是租期上限,本文分别是 600 秒和 7200 秒。移动设备多的网络可以把默认租期调短,加快地址回收。- 绑定的固定地址必须在 subnet 声明的网段内,且避开 range 动态分配范围(本文 range 从 151 起,给绑定地址留 150)。动态分配挑地址时只看 range 里的空闲位,不查 host 表——重叠了迟早冲突。
- host 声明全局生效(语法检查阶段的 WARNING 说的就是它),多网段部署时把 host 段统一写在配置文件顶层。
- 回退方案:
systemctl stop dhcpd && systemctl disable dhcpd即完全下线;配置改坏就用开头的 .backup 覆盖回去。
内网自动发地址这件事,dhcpd 一份配置文件就能兜住:动态池管普通设备,host 绑定管”必须固定 IP”的设备。部署时记两件事——绑定地址别和 range 重叠,多网段时 host 段写顶层;再加上 Rocky 9 里”不用再配 DHCPDARGS”这个新变化,剩下的就都是照着配置文件来的常规操作了。
本文由作者 2020-2024 年间的 CSDN 博客文章重构而来,原发布于 CSDN。