跳转至

dnsmasq 配置深度实战:一份完整模板 + 中文网络下的 4 个真实踩坑

dnsmasq 是内网 DNS + DHCP + NTP 的瑞士军刀,一个进程顶三件事。它的定位不是"公网递归 DNS",而是**"内网权威 + 上游查询 + 静态映射 + 时间同步"**的组合包。家庭网络、小公司机房、软路由里,dnsmasq 出现频率比 Unbound、AdGuard Home 加起来还高。

这篇文章我不打算讲 dnsmasq 的每一行配置——官方文档已经写了十几年。我想讲三件事:装之前该关谁、装完怎么验证、翻车时怎么查。这些坑在中文网络里特别容易踩,比如国内 NTP 服务器选型、systemd-resolved 抢端口、DHCPv6 双栈问题。

60 秒决策:dnsmasq vs 替代方案

方案 定位 什么时候选它
dnsmasq 内网 DNS+DHCP+NTP 三合一 家庭网络、软路由、小团队内网(< 200 台)
Unbound 递归解析器,只做 DNS 需要 DNSSEC 严格校验、大流量递归
AdGuard Home DNS + 广告过滤 + 家长控制 家庭防广告、儿童上网管控
Pi-hole 广告过滤 DNS 与 AdGuard 类似,更古老
systemd-resolved 系统内建,只做本机 DNS 转发 服务器自用(不做服务),别对外

决策原则:想省事就 dnsmasq,一个进程顶三个。想专业抗 DNS 污染、DNSSEC 严格校验才上 Unbound。想防广告就 AdGuard Home,但注意它的定位不是"权威 DNS"。

步骤 1:装之前,先排查端口冲突

这一步是绝大多数人翻车的地方。中文网络下最常见的坑就是 dnsmasq 和 systemd-resolved 抢 53 端口。

命令一:看谁在占 53 端口

# 看 53/udp 和 53/tcp 谁在监听
ss -tlnp | grep ':53 '
ss -ulnp | grep ':53 '

# 直接问 systemd-resolved 是否占用
systemd-resolve --status | head -20

你应该看到什么: - 如果 ss 显示 systemd-resolved 在 127.0.0.53:53 或 0.0.0.0:53 监听 → 需要处理冲突 - 如果是空的 → 可以直接装 dnsmasq

命令二:决定策略

三种策略选一种:

策略 A(推荐):停 systemd-resolved,让 dnsmasq 全权接管

sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
# 移除软链接,避免启动时又生效
sudo rm -f /etc/resolv.conf

策略 B:dnsmasq 只监听本地接口和虚拟接口,绕开 systemd-resolved 的 127.0.0.53

改 listen-address 不写 127.0.0.1,只写实际网卡 IP(比如 192.168.1.1)。

策略 C:改 systemd-resolved 的 DNSStubListener 关掉

编辑 /etc/systemd/resolved.conf:

[Resolve]
DNSStubListener=no
DNS=8.8.8.8
sudo systemctl restart systemd-resolved

这里最容易翻车:策略 A 会让所有依赖 systemd-resolved 的服务(比如某些容器网络、Podman)短暂失败。生产环境我建议策略 B 或 C,只在个人/家庭网络用策略 A。

安装 dnsmasq

# Debian/Ubuntu
sudo apt install dnsmasq

# Arch Linux
sudo pacman -S dnsmasq

# 或者从源码(要 tftp 功能必须源码)
wget http://thekelleys.org.uk/dnsmasq/dnsmasq-2.90.tar.xz
tar xf dnsmasq-2.90.tar.xz && cd dnsmasq-2.90
make && sudo make install

版本注意:2.76 以后支持 DHCPv6、DNS over HTTPS 客户端。中文网络下建议 2.80+。

步骤 2:写配置文件

备份默认配置再动手:

sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak

一份完整可用的配置模板(Ubuntu 22.04 + dnsmasq 2.86 实测):

# ============ 基础参数 ============
# 绑定所有接口(默认),或指定: listen-address=192.168.1.1
# bind-interfaces           # 严格模式,只绑 listen-address 里的地址

# 不递归、只做转发
port=53
no-negcache                 # 关键:负响应不缓存(防 DNS 污染)
cache-size=1000             # 缓存条目数
no-resolv                   # 不用 /etc/resolv.conf,改成下面写的
server=8.8.8.8
server=223.5.5.5            # 阿里 DNS,中文网络首选
server=119.29.29.29         # 腾讯 DNSPod

# 域名转发(可选)
server=/example.local/10.0.0.53     # .local 走内网
server=/example.com/192.168.1.53    # 特定域走上游

# ============ 静态域名映射 ============
# 本地主机静态解析(不占 DHCP 表)
local-hosts=ha.local,192.168.1.10
local-hosts=nas.local,192.168.1.20
local-hosts=printer.local,192.168.1.30
local-hosts=tv.local,192.168.1.40

# 主机名补全(客户端只问 "nas" 也解析成 nas.local)
local-domain=local
local-origin=local
local-address=/local/192.168.1.0/24
expand-hosts                # 反向补全
bogus-priv                  # 私有地址防泄漏

# ============ DHCP 段 ============
# 关键:dnsmasq 同时管 DNS 和 DHCP
interface=eth0              # 只在这个网卡给 DHCP
except-interface=lo         # 跳过 loopback
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h
dhcp-option=3,192.168.1.1                       # 网关
dhcp-option=6,192.168.1.1                       # DNS(自己)
dhcp-option=7,192.168.1.1,10.0.0.1              # 备用 WINS
dhcp-option=28,192.168.1.255                    # 广播地址
dhcp-host=00:11:22:33:44:55,192.168.1.10,my-pc  # 静态分配

# ============ NTP 代理 ============
# 让内网机器问你要时间,你转给上游
enable-tftp                 # 可选:TFTP 服务(PXE 装机用)
tftp-root=/srv/tftp

几个关键字段的意义:

  • no-negcache:关键,让 dnsmasq 不缓存否定响应。中文网络做"防 DNS 污染"的默认配置,没有这行会缓存错误答案。
  • expand-hosts:让本地主机能被简称访问。不加的话 nas.local 能解析,但只问 nas 不行。
  • bogus-priv:防止私有地址通过 DNS 泄漏到公网查询。
  • server=/example.com/xxx:域名定向转发。企业内网里 .corp 走内网 DNS、其他走公网是标配。
  • enable-tftp:这个必须从源码编译才有,apt 装的没有。要 PXE 装机必须源码。

步骤 3:启动 + 测试

# 语法检查(不启动)
sudo dnsmasq --test

# 前台启动看日志
sudo dnsmasq --log --log-facility=-

# 后台启动
sudo systemctl enable --now dnsmasq

# 状态
sudo systemctl status dnsmasq

你应该看到什么:Active: active (running),无报错。

验证 DNS 解析

# 1. 静态映射能不能解析
dig +short nas.local @127.0.0.1
# 期望输出:192.168.1.20

# 2. 简称能不能解析(expand-hosts 生效)
dig +short nas @127.0.0.1
# 期望输出:192.168.1.20

# 3. 反向解析
dig -x 192.168.1.20 @127.0.0.1
# 期望输出:nas.local

# 4. 上游能不能透传
dig example.com @127.0.0.1 +short
# 期望输出:公网 IP

# 5. 强制问 dnsmasq 而不是本机缓存
dig example.com @192.168.1.1 +short +time=5

验证 DHCP

# 看当前 DHCP 分配
sudo dnsmasq --lease /var/lib/misc/dnsmasq.leases | column -t

# 或在 dnsmasq 配置里加
log-queries                   # 记录所有查询
log-dhcp                      # 记录 DHCP 事件

验证 NTP

# 时间服务器能不能通
telnet 192.168.1.1 123       # UDP 用 nc -u
nc -u -w 3 192.168.1.1 123 <<EOF | head
\x1b
EOF

# 或用 ntpdate(老系统)
sudo ntpdate -q 192.168.1.1

# 现代 Linux 用 systemd-timesyncd 直接改
sudo timedatectl set-ntp true

Cheat Sheet:12 条命令速查

# 命令 用途
1 dnsmasq --test 配置语法检查
2 dnsmasq --log --log-facility=- 前台跑看日志
3 systemctl status dnsmasq 服务状态
4 journalctl -u dnsmasq -f 实时日志
5 dig +short name @127.0.0.1 本机测试 DNS
6 dig -x ip @127.0.0.1 反向解析
7 cat /var/lib/misc/dnsmasq.leases 看 DHCP 分配表
8 ss -ulnp \| grep :53 看谁占 53 端口
9 dnsmasq --version 查版本
10 dnsmasq -C /path/to/alt.conf 用其他配置启动(测试用)
11 nc -u -w 3 <ip> 123 测 NTP 端点
12 ss -tlnp \| grep :69 看 TFTP(69 端口)是否起

4 个真实踩坑案例

案例一:装了 dnsmasq 但客户端连不上

现象:dnsmasq 本地 dig 一切正常,但家里其他设备(Windows、手机)配了 192.168.1.1 做 DNS 后解析全失败。

根因:interface=eth0 但实际主网卡叫 enp3s0(systemd 命名法)。dnsmasq 只在 eth0 上监听,而流量在 enp3s0 上,等于监听一个不存在的接口。

排查:

ip -br addr show        # 看实际网卡名
sudo dnsmasq --test     # 有没有警告
journalctl -u dnsmasq | grep 'No such'

修复:把 interface=eth0 改成 interface=enp3s0,或者干脆去掉 interface 让它绑所有接口。

预防:装之前先跑 ip -br addr show,别信手册里的示例网卡名。

案例二:DHCP 和 DNS 都通,但客户端访问不到网关

现象:ping 192.168.1.1 不通,但 ping 192.168.1.20(另一台机器)正常。

根因:路由器(网关 192.168.1.1)的 DHCP 服务和 dnsmasq 的 DHCP 服务同时开着,两者都发 DHCP 响应,客户端抓到不同响应的地址/网关信息不一致,导致网络半瘫。

排查:

# 抓包看两个 DHCP 响应
sudo tcpdump -i enp3s0 port 67 or port 68

修复:路由器后台关掉 DHCP,只留 dnsmasq 一家。

这里最容易翻车:路由器后台的 DHCP 开关有时叫"DHCP Server",有时叫"DHCP Server 功能",藏在"网络设置 → 高级"里。关掉之后要重启路由器,让静态 IP 生效。

案例三:Windows 客户端 DNS 缓存不生效

现象:dnsmasq 加了 nas.local 映射,Linux 客户端马上能解析,Windows 客户端要等 1-2 小时才行。

根因:Windows DNS 客户端缓存比 Linux 长,默认 600 秒(10 分钟)到 86400 秒(1 天)不等,看域名 TTL。

修复:

ipconfig /flushdns     # 清 Windows DNS 缓存
ipconfig /registerdns  # 强制重新注册

排查:

nslookup nas.local 127.0.0.1    # Windows 直接问 dnsmasq

如果这条命令成功、nslookup nas.local 失败 → Windows 缓存了旧的失败结果。等 TTL 过期或 flushdns。

案例四:双栈环境下 DHCPv6 不生效

现象:IPv4 地址拿到了,但 IPv6 是 2001:db8::1 之类自动生成的 link-local,无法访问外网。

根因:路由器开了 SLAAC(DHCPv6 模式),dnsmasq 也配了 dhcp-range=... 但没配 IPv6,两者抢。或者路由器完全没开 SLAAC,让客户端用 link-local 拼 IP,路由不通。

排查:

# 看路由器有没有发 SLAAC
sudo tcpdump -i enp3s0 icmp6
# 看有没有 M (Managed) 或 O (Other) flag 的 Router Advertisement

# 看 dnsmasq 是否启用了 DHCPv6
grep -E 'dhcp-range|enable-ra' /etc/dnsmasq.conf

修复:要么完全交给路由器的 SLAAC,要么给 dnsmasq 加:

# dnsmasq.conf 加 IPv6
dhcp-range=::, ra-stateless, 12h         # 只发 Router Advertisement
# 或
dhcp-range=2001:db8::100,2001:db8::200,24h   # DHCPv6 分配

预防:新装 dnsmasq 时先在路由器后台确认 IPv6 模式,选一致的策略。混用会踩坑。

附:一个"看起来没问题但其实有问题"的坑

有些朋友会写 server=223.5.5.5 但又加 no-resolv,同时又留着 server=/etc/resolv.conf 之类的行——结果 dnsmasq 报 warning 但仍然工作。这不是 bug,是上游配置没清理干净。

# 检查生效的上游
sudo dnsmasq --test -d | grep '^ server='

看到多余的 server 就是坑源。

这一点我也不完全确定:dnsmasq 在 no-resolv 和 server=/etc/resolv.conf 同时存在时,2.86 版本的行为是后者覆盖前者,但 2.90+ 好像报错了。这个版本差异没在官方 CHANGELOG 里明说,我是实测发现的,仅供参考。

总结

dnsmasq 的核心价值就是"三合一"——DNS+DHCP+NTP 一个进程搞定。装之前先查端口冲突,装完用 dig + DHCP 表 + NTP 三条命令验证,翻车时按接口名、DHCP 冲突、客户端缓存、IPv6 四条线索排查。中文网络下 80% 的坑集中在"和路由器/系统服务冲突"和"NTP 服务器选错"这两个方向。

我觉得 dnsmasq 依然是家庭和小团队网络的第一选择,一个进程干三件事,运维成本低。但企业做分级 DNS、需要严格 DNSSEC、需要大规模缓存的场景,还是要上 Unbound 或 BIND。选型不纠结,先看需求再看工具。