plan: update

This commit is contained in:
Luna Yao
2026-02-22 01:26:53 +01:00
parent f5d3443928
commit 5de87cd494
+45 -49
View File
@@ -7,6 +7,20 @@
name = "localhost" # optional, replaces hostname, default to system hostname
domain = "localdomain" # optional, replaces tld_dns_zone, default to et.net
addresses = [
"100.100.100.101:53",
] # optional, default to [ "100.100.100.101:53" ]
# any UDP packet or ICMP packet to these addresses will be hijacked by the dns server
# the server does *not* bind to/listen on these addresses!
listeners = [
] # optional, default to empty
# let the dns server bind to these addresses
# could be useful when no_tun = true
# these two options supersede accept_dns
# setting both of them to empty is equivalent to set accept_dns = false, but zones are still broadcasted
# this policy applies to all zones with origin "example.com"
[dns."example.com".import]
whitelist = ["*"]
@@ -40,25 +54,6 @@ forwarders = [
]
```
可能会支持的配置
```toml
[dns]
addresses = [
"100.100.100.101:53",
] # optional, default to [ "100.100.100.101:53" ]
# any UDP packet or ICMP packet to these addresses will be hijacked by the dns server
# the server does *not* bind to/listen on these addresses!
listeners = [
] # optional, default to empty
# let the dns server bind to these addresses
# could be useful when no_tun = true
# these two options supersede accept_dns
# setting both of them to empty is equivalent to set accept_dns = false, but zones are still broadcasted
```
<details>
<summary><h2>计划和进展</h2></summary>
@@ -66,9 +61,9 @@ listeners = [
## protobuf
- `ZoneConfigPb`:包含所有 Zone 配置,以及一个 ID,该 ID 在读取 TOML 时生成
- `GetExportConfigResponse`:包含全部 `ZoneConfigPb`(特别地,包含专用 zone)、该 peer 的 fqdn
- `HeartbeatRequest`: DnsClient 发送的心跳,包含:id、digest、`Option<Snapshot>`
- `ZoneData`:包含 Zone 配置,以及一个 ID,该 ID 在读取 TOML 时生成
- `GetExportConfigResponse` (`DnsExportConfig`):包含全部 export 的 `ZoneData`(特别地,包含专用 zone)、该 peer 的 fqdn
- `HeartbeatRequest`: DnsNode 发送的心跳,包含:id、digest、`Option<Snapshot>`
- `DnsSnapshot`: 所有 DnsServer 需要的配置
## RoutePeerInfo
@@ -80,47 +75,51 @@ listeners = [
## DnsRunner
用于启动/监护 DnsClient 和 DnsServer
每个 EasyTier 实例都会启动一个 DnsClient,但是一台机器上的所有实例共享一个唯一的 DnsServer。
用于启动/监护 DnsNode 和 DnsServer
每个 EasyTier 实例都会启动一个 DnsNode,但是一台机器上的所有实例共享一个唯一的 DnsServer。
每个实例启动时:
1. 读取 TOML 配置,然后用这个配置启动一个 DnsClient,交给 DnsClient 一个用于尝试重启 server 的 notify
2. 尝试绑定 DNS_SERVER_RPC_ADDR 监听 RPC 请求
1. 一台机器上所有 EasyTier 实例一起尝试绑定 DNS_SERVER_RPC_ADDR,绑定成功的那个就启动 DnsServer(当然也启动 DnsClient),失败的那些就只有 DnsClient
2. 这个启动 DnsServer 的操作其实是个循环,每隔一小段时间或者 DnsClient rpc 失败(notify)后立刻尝试 bind,如果 bind 成功就说明 DnsServer 真挂了,那就自己在这个已有的 SocketAddr 上启动 DnsServer(忽略 bind 失败或启动失败,启动失败就直接释放 socket),这样才能保证服务不断
1. - [ ] 读取 TOML 配置,然后用这个配置启动一个 DnsNode,交给 DnsNode 一个用于尝试重启 server 的 notify
2. - [ ] 尝试绑定 DNS_SERVER_RPC_ADDR 监听 RPC 请求
1. 一台机器上所有 EasyTier 实例一起尝试绑定 DNS_SERVER_RPC_ADDR,绑定成功的那个就启动 DnsServer(当然也启动 DnsNode),失败的那些就只有 DnsNode
2. 这个启动 DnsServer 的操作其实是个循环,每隔一小段时间或者 DnsNode rpc 失败(notify)后立刻尝试 bind,如果 bind 成功就说明 DnsServer 真挂了,那就自己在这个已有的 SocketAddr 上启动 DnsServer(忽略 bind 失败或启动失败,启动失败就直接释放 socket),这样才能保证服务不断
## DnsClient
## DnsNode
1. 启动时(配置更新时?)将 listeners 和 addresses 添加进 delta
2. 使用自己的 name 和 domain 创建一个专用 zone,让 name 指向自身 IP,并监听 IP 地址变化事件(为 DNS 一致性避免使用 127.0.0.1 作为 IP,若没有 IP 则不创建这个 zone)
3. 每次获得 RoutePeerInfo 时,读取其中的 dns 字段(和一些别的身份标记字段),这是远程 Peer 的 dns 配置(不含 addresses 和 listeners)的 digest,接收后检查 digest 和本地配置是否一致,如果一致,不做修改,否则标记 dirty,下一次心跳时将重建快照
4. 每隔一小段时间向 DnsServer 发送心跳和当前 digest
1. - [ ] 监听配置更新/IP 地址变化,重建快照
2. - [x] (`GlobalCtx` 的扩展 trait) 使用自己的 name 和 domain 创建一个专用 zone,让 name 指向自身 IP(为 DNS 一致性避免使用 127.0.0.1 作为 IP,若没有 IP 则不创建这个 zone)
3. - [ ] 每次获得 RoutePeerInfo 时,读取其中的 dns 字段(和一些别的身份标记字段),这是远程 Peer 的 dns 配置(不含 addresses 和 listeners)的 digest
- [x] 接收后检查 digest 和本地配置是否一致,如果一致,不做修改,否则标记 dirty,下一次心跳时将重建快照
4. - [x] 每隔一小段时间向 DnsServer 发送心跳和当前 digest
1. 如果没有 dirty 标记,心跳不含 snapshot
2. 如果有 dirty 标记,重建 snapshot 并在心跳中包含;
3. 如果 DnsServer 返回 resync,立刻重新发送带有 Snapshot 的心跳
5. 一个 RPC 接口,供 Peer 拉取 DNS 配置
5. - [x] 一个 RPC 接口,供 Peer 拉取 DNS 配置
## DnsServer
1. 提供一个 RPC 接口接受 DnsClient 的心跳,如果心跳 digest 和本地不符则返回 resync
2. 收到含有 snapshot 的心跳时替换本地配置;如果 snapshot 中的 listeners 或者 addresses 不同则 rebind
3. 持续检查是否有过期(丢失心跳)的 DnsClient,需要把这些 DnsClient 提供的所有配置清除
4. 启动时自动添加 root zone,并把它的 forwarder 设置为系统 DNS
5. 使用 snapshot 更新 zone。不用合并同名 zone,直接用 Zone 结构体提供的 ChainedAuthority 按顺序插入 Catalog 就行,不过注意要先插入 MemoryAuthority,这些都是 records,后插入 ForwardAuthority,这都是 forwarders
6. 更新 zone 的时候自动去掉 forwarder 中导致回环的那些,就是把 addresses 和 listeners 去掉(root zone 也需要这个逻辑)
7. 内部接口,控制 DnsServer 是否 bind 到某些 socket(也就是配置中的 listeners
8. Listeners 绑定失败打印日志(失败一个打印一次然后就跳过),即便这时 addresses 为空也不要停机。(否则释放 socket 绑定后会有 instance 抢占 socket 试图启动 server,然后就死循环
9. 内部接口,更新 addresses,并且给 tun 添加删除这些 addresses 的路由。目前这些用来 hijack 的 addresses 都是只支持 udp 简单查询,就是一个 UDP 包查询,tcp 完全不管。但是可以支持除了 53 之外的端口,这个不难。
10. 启动时,往 packet pipeline 上挂一个 filter,和目前 magic dns 的操作一样,给 addresses 添加路由并劫持所有目的为配置中 addresses 的 UDP 包,直接作为 DNS request 读取并交给 DnsServer 解析(这个 addresses 可能还得 append 到 resolv.conf 之类的地方)
11. Addresses 和 Listeners 更新时需要检查所有 zone 的 forwarder,之前为了避免回环可能去掉了一些 forwarder,或者有新的 forwarder 要去除
1. - [x] 提供一个 RPC 接口接受 DnsNode 的心跳,如果心跳 digest 和本地不符则返回 resync
2. - [x] 收到含有 snapshot 的心跳时替换本地配置;如果 snapshot 中的 listeners 或者 addresses 不同则 rebind
3. - [x] (`moka::Cache`) 持续检查是否有过期(丢失心跳)的 DnsNode,需要把这些 DnsNode 提供的所有配置清除
4. - [x] 每次更新 zone 时自动添加 root zone
- [x] (`Zone::system`) 并把它的 forwarder 设置为系统 DNS
5. - [x] 使用 snapshot 更新 zone。不用合并同名 zone,直接用 Zone 结构体提供的 ChainedAuthority 按顺序插入 Catalog 就行,不过注意要先插入 MemoryAuthority,这些都是 records,后插入 ForwardAuthority,这都是 forwarders
6. - [x] 更新 zone 的时候自动去掉 forwarder 中导致回环的那些,就是把 addresses 和 listeners 去掉(root zone 也需要这个逻辑
7. - [x] 内部接口,控制 DnsServer 是否 bind 到某些 socket(也就是配置中的 listeners
8. - [x] Listeners 绑定失败打印日志(失败一个打印一次然后就跳过),即便这时 addresses 为空也不要停机。(否则释放 socket 绑定后会有 instance 抢占 socket 试图启动 server,然后就死循环)
9. - [x] 内部接口,更新 addresses。目前这些用来 hijack 的 addresses 都是只支持 udp 简单查询,就是一个 UDP 包查询,tcp 完全不管。但是可以支持除了 53 之外的端口,这个不难。
- [ ] 并且给 tun 添加删除这些 addresses 的路由
10. - [x] 启动时,往 packet pipeline 上挂一个 filter,和目前 magic dns 的操作一样,给 addresses 添加路由并劫持所有目的为配置中 addresses 的 UDP 包,直接作为 DNS request 读取并交给 DnsServer 解析
- [ ] 这个 addresses 可能还得 append 到 resolv.conf 之类的地方
11. - [x] Addresses 和 Listeners 更新时~~需要检查所有 zone 的 forwarder~~直接更新所有 zone,之前为了避免回环可能去掉了一些 forwarder,或者有新的 forwarder 要去除
此外,还有以下几个设计要点:
- Zone 允许只有 forwarder,这时候就是纯转发器
- Zone 允许没有 forwarder,这时候要检查是不是有 SOA 和 NS 记录,如果没有可能需要添加?
- 另一种方案是 DnsClient 挂 filter,自己处理 UDP 劫持,用某种方式(如 RPC)把 DNS 请求代理给 DnsServer,该方案的优势在于完全解耦 DnsServer 的实现,特别是解决了 DnsServer 所在实例可能 no_tun 的问题,缺点是:
- 另一种方案是 DnsNode 挂 filter,自己处理 UDP 劫持,用某种方式(如 RPC)把 DNS 请求代理给 DnsServer,该方案的优势在于完全解耦 DnsServer 的实现,特别是解决了 DnsServer 所在实例可能 no_tun 的问题,缺点是:
- 性能更差
- 操作路由表或 /etc/resolv.conf 时会有多个 instance 同时修改,修改结果没有确定性
- DnsServer 仍然需要得知 addresses 以进行回环检测
@@ -143,9 +142,6 @@ listeners = [
## TODO
- [x] 配置解析
- [ ] DnsClient
- [ ] DnsServer
- [ ]`RoutePeerInfo` 传播配置 hash,用 RPC 拉取配置
- [ ] cli 输出状态
</details>