目 录CONTENT

文章目录

1 分钟定位 OpenWrt 全网解析失败:检查 dnsmasq crash loop

OpenWrt:dnsmasq 因 IDN(非 ASCII 域名)配置语法错误导致 LAN DNS 崩溃的排障记录

说明:本文仅讨论 OpenWrt 上 dnsmasq 的配置兼容性与故障排查,不涉及也不提供任何绕过网络访问限制的使用指导。
关键词:OpenWrt / dnsmasq / IDN / punycode / crash loop / /tmp/dnsmasq.d

结论先行:某些组件会自动生成 dnsmasq 片段配置;当片段里出现 中文域名(IDN,非 ASCII) 时,可能触发 dnsmasq 语法错误 → 启动失败 → LAN 侧 DNS 解析整体不可用,用户侧观感就是“全网像断网”。


1 分钟快速定位(遇到“启用某组件后全网像断网”先看这个)

  1. 先确认是不是 DNS 服务本身挂了:
logread -e dnsmasq
  1. 如果你看到类似关键词,基本就是 dnsmasq 在 crash loop:
  • error at line ... of /tmp/dnsmasq.d/...
  • FAILED to start up
  • dnsmasq in a crash loop
  1. 直接打开日志里提示的那个文件路径,检查是否出现非 ASCII/中文域名行:
# 把下面路径替换成 logread 里显示的实际路径
sed -n '1,120p' /tmp/dnsmasq.d/xxx.conf
  1. 临时删掉/过滤掉“非 ASCII 域名”那几行后,重启 dnsmasq:
/etc/init.d/dnsmasq restart
logread -e dnsmasq

目录


一、问题背景

OpenWrt 上常见的网络能力(例如:域名策略、按域名打标、基于 ipset/规则集的分流等)经常会借助 dnsmasq 的片段配置实现:

  • /tmp/dnsmasq.d/ 下动态生成额外配置
  • dnsmasq 启动时会加载这些片段

这类“自动生成配置”的机制很方便,但也带来一个典型风险:生成内容不符合 dnsmasq 语法 → dnsmasq 直接起不来


二、现象与初步判断

1)现象

启用某个“会生成 dnsmasq 片段配置”的组件后,局域网内出现:

  • 解析域名失败(大部分网站/服务都打不开)
  • 终端表现像“断网”,但物理链路、DHCP 可能仍是好的
  • 路由器页面/组件页面可能提示 “DNS 异常”

2)初步判断

如果你确认:

  • WAN 拨号/上网正常(路由器本身能 ping 通 IP)
  • 但“所有依赖域名的访问”都失败

优先把排障焦点放在 LAN DNS(dnsmasq)是否正常运行,而不是先去改网络出口或其它组件参数。


三、关键突破:dnsmasq crash loop

查看 dnsmasq 日志:

logread -e dnsmasq

如果反复出现:

  • error at line ... of /tmp/dnsmasq.d/...
  • FAILED to start up
  • dnsmasq in a crash loop

关键结论:dnsmasq 没有成功启动。

在 OpenWrt 的默认架构下:

  • dnsmasq 通常承担 LAN 侧 DNS(以及常见的 DHCP/DNS 缓存能力)
  • dnsmasq 一旦挂掉 ⇒ 整个局域网域名解析“归零”
  • 用户侧体验就会非常像“全网断网”

四、定位崩溃源头:自动生成的 dnsmasq 片段

日志里一般会指出具体文件,比如:

  • /tmp/dnsmasq.d/<某目录>/<某文件>.conf
  • 或者 /tmp/dnsmasq.d/<某文件>.conf

直接打开对应文件,查看报错行附近内容:

# 示例:查看前 120 行,必要时扩大范围
sed -n '1,120p' /tmp/dnsmasq.d/xxx/001-server.conf

常见会看到类似规则(示例仅为说明语法形态):

server=/.example.com/127.0.0.1#5353
ipset=/.example.com/some_set

五、根因分析:IDN(中文域名)触发 dnsmasq 语法错误

当自动生成的片段里出现 非 ASCII 域名(例如中文域名),就可能触发 dnsmasq 语法错误,导致启动失败。

示例(仅示意问题,不建议这样写):

server=/.例子.测试/127.0.0.1#5353
ipset=/.例子.测试/some_set

为什么会出错

  • dnsmasq 的域名匹配规则在很多场景下要求域名为 ASCII(或以 punycode:xn--... 形式表达 IDN)
  • 直接把中文域名“裸写进配置片段”,部分版本/编译选项下会被解析为非法字符,进而报语法错误
  • 一旦有 任意一个加载的片段配置语法错误,dnsmasq 可能直接拒绝启动

六、紧急止血:先让 dnsmasq 活过来

目标:先恢复 LAN DNS,避免“全网像断网”。

1)快速定位“非 ASCII 行”

awk 找出包含非 ASCII 字符的行(BusyBox 一般可用):

awk '($0 !~ /^[[:print:][:space:]]*$/) || ($0 ~ /[^\x00-\x7F]/) {print NR ":" $0}' /tmp/dnsmasq.d/xxx/001-server.conf

或直接粗暴搜索(如果系统 grep 支持):

grep -n '[^ -~]' /tmp/dnsmasq.d/xxx/001-server.conf

2)临时过滤掉包含非 ASCII 的行(不改其它内容)

建议先备份一份:

cp /tmp/dnsmasq.d/xxx/001-server.conf /tmp/001-server.conf.bak

然后过滤(保留纯 ASCII 行):

awk '/^[\x00-\x7F]*$/' /tmp/dnsmasq.d/xxx/001-server.conf > /tmp/001-server.conf.fixed
mv /tmp/001-server.conf.fixed /tmp/dnsmasq.d/xxx/001-server.conf

对其它同类片段文件同样处理(以日志里点名的文件为准)。

3)重启 dnsmasq 并验证

/etc/init.d/dnsmasq restart
logread -e dnsmasq

看到类似 Connected to system UBus / started 之类信息,通常说明已恢复。


七、长期方案:输入域名全部转为 ASCII(punycode)或做过滤

临时改 /tmp/... 的文件通常会复发,因为这些文件经常是运行时重新生成的。

更稳的思路有两条:

方案 A:源头治理(推荐)

确保“会被写入 dnsmasq 片段”的域名列表 全部为 ASCII / punycode

你可以在 PC 上把 IDN 转 punycode(举例):

  • 例子.测试xn--fsqu00a.xn--0zwm56d(示例)

然后把组件侧的域名源(规则/列表/自定义域名等)统一替换成 punycode 形式。

方案 B:生成后过滤(折中)

如果你无法控制源头(例如某些列表来自外部、或组件每次都会自动写入),可以在“dnsmasq 启动前/网络事件后”做一次过滤,保证 dnsmasq 永远不因非 ASCII 行而启动失败。

示例:过滤某目录下的片段(仅作思路演示)

注意:你需要把 TARGET_DIR 换成你设备上实际的生成目录(从 logread -e dnsmasq 获取)。

TARGET_DIR="/tmp/dnsmasq.d/xxx"

for f in "$TARGET_DIR"/*.conf; do
  [ -f "$f" ] || continue
  awk '/^[\x00-\x7F]*$/' "$f" > "$f.ascii" && mv "$f.ascii" "$f"
done

/etc/init.d/dnsmasq restart

八、复发预防清单

  • 每次“像断网”先看 logread -e dnsmasq,确认是不是 crash loop
  • 以日志点名的 /tmp/dnsmasq.d/... 文件为准定位问题片段
  • 检查片段里是否出现 IDN / 非 ASCII
  • 长期把域名源统一为 punycode(xn--...)
  • 如无法源头治理,用“生成后过滤”确保 dnsmasq 可启动
  • 任何自动化脚本都建议先备份、并限定作用范围(只动特定目录/特定文件)

九、排障命令速查表

dnsmasq 日志

logread -e dnsmasq

查看报错文件内容(以日志提示路径为准)

sed -n '1,120p' /tmp/dnsmasq.d/xxx/xxx.conf

找出非 ASCII 行

grep -n '[^ -~]' /tmp/dnsmasq.d/xxx/xxx.conf
# 或(更通用)
awk '($0 ~ /[^\x00-\x7F]/) {print NR ":" $0}' /tmp/dnsmasq.d/xxx/xxx.conf

仅保留 ASCII 行(临时止血)

awk '/^[\x00-\x7F]*$/' /tmp/dnsmasq.d/xxx/xxx.conf > /tmp/xxx.fixed
mv /tmp/xxx.fixed /tmp/dnsmasq.d/xxx/xxx.conf

重启 dnsmasq

/etc/init.d/dnsmasq restart
0

评论区