daeuniverse / dae

eBPF-based Linux high-performance transparent proxy solution.
GNU Affero General Public License v3.0
2.63k stars 165 forks source link

[Enhancement] UDP 没有连接追踪, 回复流量可能被代理 (进入dae0) #475

Closed LostAttractor closed 2 months ago

LostAttractor commented 3 months ago

Improvement Suggestion

问题

UDP是无连接 且 无状态的, 但往往UDP连接也是成对的: 一个请求 对应 一个回复 例如对于

client.port -> server.port

会期望产生如下回复:

server.port -> client.port

这常见但不限于 DNS 和 QUIC

DAE目前无法追踪UDP的状态, 这意味着发往 lan路由(dae所在设备) 的 UDP连接 的 回复 可能被代理 例如我们考虑如下拓扑:

client <--(wan)--> router(dae) <--(lan)--> server

然后对server产生一个DNS查询, 它会是:

client.port -> router(dae) -> server.53
server.53 -> router(dae) -> client.port

对于请求(第一步), 在dae看来是inbound, 自然直接不会处理 对于返回(第一步), 在dae看来是outbound, 因为没有连接追踪, dae无法根据第一步来判断这是回复, 因此会正常在做eBPF中的路由流程 而如果被分流到了代理, 则会进入到dae0, 从代理发出

最终变成:

client.port -> router(dae) -> server.53
server.53 -> router(dae) --(bpf_redirect)--> proxy_server.port -> client.port

虽然 client 的对应接口也能收到回复, 但因为是 proxy_server 进行的回复, 回复源IP和请求时的目标IP不一致, 自然不会理会结果

因此判断连接是一个新连接, 还是某一个连接的回复是必要的, 即需要UDP的连接追踪

因为 TCP 是有状态且面向连接的, 内核实现了类似功能, 而 DAE 会根据内核提供的状态对回复流量做绕过, 所以TCP回复可以正常工作

Netfiler 是如何工作的

在 nft 中, nft 自己实现了 UDP 的连接追踪, conntrack功能具有ct state这个状态 即对于任意一个 a.port -> b.port 的UDP连接, 它的ct state会是newb.port -> a.port的数据路径相反的连接, 它的ct state会是established

这才使得如下规则可以让自己进行的 UDP 请求可以正常回复, 而其他UDP入站被丢弃

chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept
}

因此, 我们在基于netfiler的代理中可以单独绕过 UDP 请求的回复, 保证回复流量不被代理

可能的解决方案

要实现类似的连接追踪, 至少需要维护一个表, 监控所有来源是wan接口的UDP流量, 并做记录 而对目标是wan接口的流量, 判断是否有一个近期的连接, 数据路径完全相反, 如果有, 则绕过 因为要维护表用于判断近期的连接, 还需要考虑如何实现老化

此外, 可能考虑会有不绑定 wan接口 或 各种 入站/回复流量序列 不经过 设置的 wan接口 的情况 可能也要对 lan接口 也做如上操作

Potential Benefits

可以使得 UDP 回复不需要做特判就可以正常工作而不被代理

如果 UDP 无法正常回复, 很多应用都无法工作

dae-prow[bot] commented 3 months ago

Thanks for opening this issue!

mzz2017 commented 3 months ago

@LostAttractor 我们正在探讨表项老化的实现,使用控制面双桶方案和内核 bpf timer 的方案的优劣,后者需要 5.15+。具体方案之后会 post 上来。

mzz2017 commented 2 months ago

@LostAttractor 可以来测试一下 #493 吗