0%

OpenWrt 部署 ISC-DHCP-Server 全功能 DHCPv6 服务器

业务需要搭建一个 DHCPv6 测试环境,因为手上资源有限,于是决定使用 OpenWrt 的路由器来操作。

测试环境的目标是针对 Android 设备连接 WiFi 的情况下,验证 DHCPv6 的工作情况。

本文提到的 OpenWrt 代指所有运行 OpenWrt 的 24.10.4 版本固件的路由器。

软件包准备

OpenWrt 默认情况下使用 odhcpd 作为 DHCP 服务器。

odhcpd 的功能满足 OpenWrt 的基本要求,但是不满足需要的测试场景,比如不能自定义控制 DHCPv6 的地址范围等参数。

所以需要搭配一个强劲的 DHCPv6 服务器软件。

在 OpenWrt 中安装以下软件包:

  • dnsmasq-dhcpv6
  • isc-dhcp-server-ipv6
    这个包包含了 isc-dhcp-server-ipv4 的功能。

通过界面配置

修改 lan 接口的 IPV4 地址

  • 网络 -> 接口 -> lan -> 常规设置 -> IPv4 地址 -> 192.168.10.1

这个步骤对 DHCPv6 测试没有直接关系,如果不关注 DHCPv4,可以不修改。

配置 lan 接口的 odhcpd 服务,禁用 DHCPv4、DHCPv6 以及 RA中关闭SLAAC并开启M+O

  • 网络 -> 接口 -> lan -> DHCP服务器 -> 常规设置 -> 忽略此接口 -> 勾选
    禁用 odhcpd 对 br-lan 开启的 DHCPv4 服务。如果不关注 DHCPv4,可以不修改。
  • 网络 -> 接口 -> lan -> DHCP服务器 -> IPv6设置 -> RA服务 -> 服务器模式
    控制 RA 来源。
  • 网络 -> 接口 -> lan -> DHCP服务器 -> IPv6设置 -> DHCPv6服务 -> 已禁用
    禁用 odhcpd 对 br-lan 开启的 DHCPv6 服务。
  • 网络 -> 接口 -> lan -> DHCP服务器 -> IPv6 RA 设置 -> 默认路由器 -> 强制通告
    控制 Android 设备无线网卡是否具备默认路由。
  • 网络 -> 接口 -> lan -> DHCP服务器 -> IPv6 RA 设置 -> 启用SLAAC -> 取消勾选
    模拟没有 SLAAC 或具有无效 SLAAC 的情况。
  • 网络 -> 接口 -> lan -> DHCP服务器 -> IPv6 RA 设置 -> RA标记 -> MO
    开启 M
    O 模式,告知客户端启动 DHCPv6 流程。

Android 设备 AOSP 原生实现默认不会检查 M & O & P 的配置,而是根据是否具有默认路由来判断是否启动 DHCPv6 流程。

修改 lan 接口的 IPV6 地址

  • 网络 -> 接口 -> 全局网络选项 -> IPv6 ULA 前缀 -> 2001:758:1:1::/64
    配置 ULA 前缀,以一个公网前缀开头,模拟公网环境。

通过终端配置

由于 isc-dhcp-server 没有前台界面,所以只能通过终端进行配置。使用终端连接路由器 ssh root@192.168.10.1,密码是路由器后台密码。

初始化 isc-dhcp-server 运行环境

mkdir -p /var/db/
touch /var/db/dhcpd.leases
touch /var/db/dhcpd6.leases

初始化配置文件

由于 isc-dhcp-server 会通过配置文件规划的网段自动匹配网卡,所以这里使用的是和 br-lan 相同的网段。这也是修改全局 IPV6 地址的原因。

关于绑定网卡,还有种说法是新建 /etc/default/isc-dhcp-server 文件,可以在其中指定要监听的网卡,不过我这尝试过,发现并不管用,可能还要哪里遗漏了什么:

# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)

# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
#DHCPDv4_CONF=/etc/dhcp/dhcpd.conf
#DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf

# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
#DHCPDv4_PID=/var/run/dhcpd.pid
#DHCPDv6_PID=/var/run/dhcpd6.pid

# Additional options to start dhcpd with.
#       Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
#OPTIONS=""

# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
#       Separate multiple interfaces with spaces, e.g. "eth0 eth1".
INTERFACESv4="br-lan"
INTERFACESv6="br-lan"

创建 /etc/dhcpd.conf 文件并写入以下内容:

tee /etc/dhcpd.conf <<\EOF
# /etc/dhcpd.conf

subnet 192.168.10.0 netmask 255.255.255.0 {
        # Range for clients
        range 192.168.10.100 192.168.10.200;

        # Additional options
        option domain-name-servers 192.168.10.1;
        option domain-name "domain.example";
        option routers 192.168.10.1;

        default-lease-time 600;
        max-lease-time 7200;
}
EOF

/etc/dhcpd.conf 是负责 DHCPv4 的配置。如果不关注 DHCPv4,可以不创建。

创建 /etc/dhcpd6.conf 文件并写入以下内容:

tee /etc/dhcpd6.conf <<\EOF
# /etc/dhcpd6.conf

authoritative;

default-lease-time 3600;
max-lease-time 86400;

# Enable RFC 5007 support
#allow leasequery;

# Global definitions for name server address(es) and domain search list
#option dhcp6.name-servers 3ffe:501:ffff:100:200:ff:fe00:3f3e;
#option dhcp6.domain-search "test.example.com","example.com";

# Set preference to 255 (maximum) in order to avoid waiting for
# additional servers when there is only one
#option dhcp6.preference 255;

# Server side command to enable rapid-commit (2 packet exchange)
#option dhcp6.rapid-commit;

# The delay before information-request refresh
#  (minimum is 10 minutes, maximum one day, default is to not refresh)
#  (set to 6 hours)
#option dhcp6.info-refresh-time 3600;

subnet6 2001:758:1:1::/64 {
        # Range for clients
        range6 2001:758:1:1:1:: 2001:758:1:1:1::ffff;

        # Range for clients requesting a temporary address
        range6 2001:758:1:1:2:: /112 temporary;

        # Prefix range for delegation to sub-routers pd
        prefix6 2001:758:1:1:3:: 2001:758:1:1:4:: /80;

        # Additional options
        option dhcp6.name-servers 2001:758:1:1::1;
        option dhcp6.domain-search "domain.example";
}
EOF

调整服务开机自启动顺序

实测发现默认的开机启动时机太早,会导致服务启动后绑定网卡失败。

将 isc-dhcp-server 服务开机启动顺序放到靠后的位置(原理请看 /etc/rc.common & /etc/rc.d/S*):

# 取消设置开机自启
service dhcpd disable
service dhcpd6 disable

# 修改开机自启顺序,原始:dhcp START=25 dhcp6 START=65
sed -i 's/START=\d\+/START=99/g' /etc/init.d/dhcpd
sed -i 's/START=\d\+/START=99/g' /etc/init.d/dhcpd6

# 重新设置开机自启
service dhcpd enable
service dhcpd6 enable

dhcpd 是负责 DHCPv4 的服务。如果不关注 DHCPv4,可以不操作。
https://github.com/openwrt/packages/tree/openwrt-22.03/net/isc-dhcp

启动服务

service dhcpd start
service dhcpd6 start

dhcpd 是负责 DHCPv4 的服务。如果不关注 DHCPv4,可以不操作。

通过以下命令查看 dhcpd 服务日志:

logread | grep dhcpd
Wed Dec 10 19:25:26 2025 daemon.info dhcpd: Bound to *:547
...
Wed Dec 10 19:25:26 2025 daemon.info dhcpd: Server starting service.

检查一下启动的服务情况:

service

正常结果如下,dhcpd 与 dhcpd6 处于运行状态。

Usage: service <service> [command]
...
/etc/init.d/dhcpd                  enabled         running
/etc/init.d/dhcpd6                 enabled         stopped
...

这里的 stopped 不必理会,是软件包适配问题,可通过 netstat 命令确认。

检查一下启动的服务网络情况:

netstat -tplnu

正常结果如下,dhcpd 绑定在 0.0.0.0:67(DHCPv4) 和 :::547(DHCPv6)。

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
udp        0      0 0.0.0.0:67              0.0.0.0:*                           8087/dhcpd
udp        0      0 :::547                  :::*                                7582/dhcpd

结果验证

终端验证

  • ifconfig
    wlan0     Link encap:Ethernet  HWaddr d0:a0:d6:1b:3a:e8  Driver wlan
              inet addr:192.168.10.100  Bcast:192.168.10.255  Mask:255.255.255.0
              inet6 addr: fe80::2623:b587:4f56:e6a/64 Scope: Link
              inet6 addr: 2001:758:1:1:1::fda5/128 Scope: Global
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:19 errors:0 dropped:0 overruns:0 frame:0
              TX packets:27 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:3000
              RX bytes:2865 TX bytes:3163
    
  • ip address show dev wlan0
    36: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 3000
        link/ether d0:a0:d6:1b:3a:e8 brd ff:ff:ff:ff:ff:ff
        inet 192.168.10.100/24 brd 192.168.10.255 scope global wlan0
           valid_lft forever preferred_lft forever
        inet6 2001:758:1:1:1::fda5/128 scope global nodad dynamic noprefixroute
           valid_lft 3095sec preferred_lft 1745sec
        inet6 fe80::2623:b587:4f56:e6a/64 scope link stable-privacy
           valid_lft forever preferred_lft forever
    
  • ping6 -c 4 2001:758:1:1::1
    PING 2001:758:1:1::1(2001:758:1:1::1) 56 data bytes
    64 bytes from 2001:758:1:1::1: icmp_seq=1 ttl=64 time=7.94 ms
    64 bytes from 2001:758:1:1::1: icmp_seq=2 ttl=64 time=7.89 ms
    64 bytes from 2001:758:1:1::1: icmp_seq=3 ttl=64 time=10.2 ms
    64 bytes from 2001:758:1:1::1: icmp_seq=4 ttl=64 time=8.42 ms
    
    --- 2001:758:1:1::1 ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 3007ms
    rtt min/avg/max/mdev = 7.894/8.635/10.287/0.979 ms
    
  • ip -6 route show table 0
    2001:758:1:1:1::fda5 dev wlan0 table wlan0 proto static metric 1024 pref medium
    2001:758:1:1::/64 dev wlan0 table wlan0 proto kernel metric 256 expires 5270sec pref medium
    2001:758:1:1::/64 dev wlan0 table wlan0 proto static metric 1024 pref medium
    fe80::/64 dev wlan0 table wlan0 proto kernel metric 256 pref medium
    fe80::/64 dev wlan0 table wlan0 proto static metric 1024 pref medium
    default via fe80::ee88:8fff:febb:9182 dev wlan0 table wlan0 proto ra metric 1024 expires 2570sec hoplimit 64 pref medium
    2001:758:1:1:1::fda5 dev wlan0 table wlan0_local proto static metric 1024 pref medium
    2001:758:1:1::/64 dev wlan0 table wlan0_local proto static metric 1024 pref medium
    fe80::/64 dev wlan0 table wlan0_local proto static metric 1024 pref medium
    fe80::/64 dev dummy0 table dummy0 proto kernel metric 256 pref medium
    default dev dummy0 table dummy0 proto static metric 1024 pref medium
    local ::1 dev lo table local proto kernel metric 0 pref medium
    local 2001:758:1:1:1::fda5 dev wlan0 table local proto kernel metric 0 pref medium
    local fe80::2623:b587:4f56:e6a dev wlan0 table local proto kernel metric 0 pref medium
    local fe80::d8b3:36ff:fe3a:3c4c dev dummy0 table local proto kernel metric 0 pref medium
    multicast ff00::/8 dev dummy0 table local proto kernel metric 256 pref medium
    multicast ff00::/8 dev wlan0 table local proto kernel metric 256 pref medium
    

抓包验证

android_dhcpv6_tcpdump.cap