0%

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

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

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

本文提到的 OpenWrt 代指所有运行 OpenWrt24.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

正常结果如下,dhcpddhcpd6 处于运行状态。

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