eh5 最近的时间轴更新
eh5

eh5

V2EX 第 303203 号会员,加入于 2018-03-25 16:59:46 +08:00
eh5 最近回复了
@s82kd92l
不是很了解 Open vSwitch 和 OpenFlow ,但粗略的看了下似乎是基于匹配规则进行包的转发,而且它的 NAT 似乎也是基于 Netfilter conntrack 的,见 https://ovs-istokes.readthedocs.io/en/latest/tutorials/ovs-conntrack.html

但要实现 Endpoint-Independent Mapping 还是需要实时地在包解析阶段就查询更新 mapping 表。如果不使用 conntrack 通过用户态的监控并(滞后地?)添加映射规则那实时性肯定不够的。而如果使用 conntrack 那和我上面所说在 eBPF 中动态维护 conntrack 表没有区别,你只能去控制 filtering 行为而无法控制 mapping 行为,最终结果还是会回落到 Netfilter conntrack 系统的 “Address and Port-Dependent Mapping” 行为。

如果 会有某一天 kernel 实现了 EIM + EIF ,那也只会是独立于 conntrack 的另外一套系统,因为 conntrack 本来就是 Address and Port-Dependent 的。
@Jirajine

> 具体到通过策略路由把目的为本机的包强行发到外部接口上,这已经 break 了其他所有人预期的行为

所以你不是已经知道了么?默认的规则已经覆盖了最常见的场景了,你对于自己的网络有特殊配置完全可以关闭 hairpin 或者定制你自己的路由规则不是么,请注意这个主题的内容是关于 einat ,如果你对于任何 einat 的实现有建议意见提升方法的话请发 issue 。

> 这种转发会非常严重的降低性能(这还没有考虑 ebpf 本身的开销,在嵌入式设备上应该也是不可忽略的)

请你先 profile 一下给出比较数据,或者给出 source 。

> NAT64

关于你对 NAT64 的话题已经 off-topic 了,我也不会再重复我之前的观点(当然你也没有看到我的侧重点而是单方面的输出),请单发主题或者在 https://github.com/EHfive/einat-ebpf/issues/3 下给出评论。

> 说道性能,在常用的家用嵌入式路由器的 cpu 根本无法支持高带宽场景下的 nat ,而是 offload 到专用硬件处理

off-topic ,偷换概念,比较的对象应该是 Netfilter conntrack SNAT/DNAT 和 einat 而不是 NAT offloading 和 einat 。

鉴于你一直无视主题单方面输出观点,我在此主题不会再回复你的评论。
@maybeonly
> 但因为 einat 如你所说是透明的
*但因为 einat 如 @Jirajine 所说是透明的

抱歉,刚起床迷糊把你看成他了。。
> 但是如果这些端口和其他 dnat 规则冲突,抑或被其他程序占用呢
> 可能不得不特别小心 dnat 的选择,以及用 ip_local_port_range 隔离了

嗯,对于本来预想接受初始入站连接的端口(不管是本机的监听服务还是对外部地址端口的 DNAT )是应该被排除在 einat 的 NAT 端口范围外。我当初也是预想到了这种情况所以在 einat 中实现了端口范围限定,并且对 TCP 和 UDP 以 20000-29999 为默认端口范围从而隔离可能常用的低端口和 ip_local_port_range (默认 32768-61000 )。

einat 对于范围内的端口则是严格执行 EIM + EIF ,其中重要的一点是只允许在有从内部发出的初始出站连接后才能打开相应的源端口接受后续其他入站连接。而且即使对于以网卡外部地址+范围内端口为 source 的初始出站连接也是如此(即范围内的端口有可能被重映射,比如 egress: 20000 -> 20001, ingress: 20001 -> 20000 ),但因为 einat 如你所说是透明的,这其实和其他以私有地址为来源的出战连接没多少不同,见 https://github.com/EHfive/einat-ebpf/blob/9e6f8e6720c7244f40693e2bd119d268b855afd5/src/bpf/einat.bpf.c#L1879-L1892

PS:话说我应该在博客里写这些的。。
@Jirajine

> 如果我理解的不错的话,把这个 ebpf 挂载到网卡上之后,对网络栈的其他部分而言完全透明,就好像根本没有使用 nat ,来自的内网地址的包会被公网的路由器发回来一样。

是的,我应该提一嘴 einat 是从头开始写的不依赖 conntrack 的独立 NAT 实现,einat 的 README 里也有稍微提到,https://github.com/EHfive/einat-ebpf?tab=readme-ov-file#alternatives

最开始的版本的确是类似一些 eBFP 负载均衡应用通过维护 Netfilter conntrack 实现的,但是 eBFP 无法控制初始 conntrack 的源端口分配所以并不能把 “Address and Port-Dependent Mapping” 的 conntrack 系统限制为 “Endpoint-Independent Mapping”,所以初始版本是一个失败的实现,见 https://github.com/EHfive/einat-ebpf/tree/legacy 。于是就决定从头写一个独立的 NAT 实现。
@Jirajine

> 对于 masquarade 而言根本不需要做这种事情,没有哪个 p2p 的应用需要从内网访问映射的端口而不是直接连接

RFC 4787 ,REQ-9: A NAT MUST support "Hairpinning"

需要,而且很需要,如果没有 Hairpin ,你自己的外部地址+端口外部可以访问但内部不能访问不是很奇怪么,而且这个内部包括整个 NAT 后面的网络。没有 Hairpin ,内部网络间的 P2P 就无法联通。

> 这样做肯定会和很多东西冲突的,比如本地监听的程序和 netfilter dnat

你有点想当然了,hairpin 的流量最终是会从网卡返回来的,对于需要 NAT 的流量会进行 NAT ,不需要的流量会最终 loopback 回来,这个路由规则相当于在正常的 (lan -> local) 包流中加入 einat (lan -> einat > local),并不会有什么所谓的冲突。

> 网络就不要部署以 ipv4 地址作为 host 的 http 服务啊

这不是你可以控制的,没有域名使用 IPv4 作为地址的公共网站还是有的,用户也有需求访问这些网站。而且可以嵌入 L3 IPv4 地址的协议也不止 HTTP ,比如 BT 。对于家庭/办公室网络这些都是刚需。

> 现代系统会自己自动进行本地 464XLAT

我不知道有哪个 Linux 发行版默认配置了这个,而且家庭用户的设备也不一定是高集成度的”现代系统“。

关于 NAT64 你单方面的阐述它对你限制性应用场景下方便网络配置的必要性就到此为止吧,这差不多跑题了,建议另开一主题先让大家达成共识。

而且你我都不在一个频道上,我的论点是 NAT64 下的 IPv6-only 对于家庭用户来说为了配置它所付出的并不能得到相比传统双栈网络在网络连通性、速度方面的提升,作为家庭用户我个人并没有动机去使用它并花费精力实现它,所以对于在 einat 中实现 NAT64 的优先级为低。

我对 einat 预想的使用场景充其量是家庭和办公室网络,而且非图灵完备的 eBPF 程序能实现的功能是有限制的,对于其他场景和更高级的功能建议使用企业级 NAT 设备。。
@Jirajine

https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks 中的图表为示意图,
einat 和 Netfilter DNAT/SNAT 的区别是前者 NAT 发生在出口网卡的 ingress 和 egress ,后者发生在 Prerouting 和 Postrouting ,所以只要你的规则本来就在这之间不会有区别。当然如果你配置了 hairpin 路由配置那对你的策略路由或许会有影响,具体路由见配置 hairpin 后的 `ip rule`,另见 https://github.com/EHfive/einat-ebpf/issues/4#issuecomment-2001996895

> 通过 nat64 能部署纯 ipv6 内网最大的好处就是简化双栈网络的复杂度,防火墙/路由/地址规则不用每个主机都重复两遍

NAT64 对于业务组网确实有好处,但应用必须依赖 DNS64 ,对于有限制的业务网络当然没什么问题。 然而如果应用内附了 L3 IPv4 信息,比如以 IPv4 地址作为 Host 的 HTTP 服务(对于家庭/办公室网络是很常见的场景),那为了访问这些 IPv4-only 的应用必然得在终端设备配置静态 NAT46 ,而且也不可能覆盖所有非通用设备,那最后还是需要冗余的双栈网络覆盖所有终端设备。你说的简化网络复杂度和方便配置规则的好处也就不成立了。
@maybeonly 哦,这个确实,那目前要改 MTU 只能把相关转发的网卡也改了,避免在 einat 处理拆包,不过 BPF 里或许可以通过 `BPF_FIB_LKUP_RET_FRAG_NEEDED` 知道 MTU 信息从而发回 PMTU 错误包,不过这又是一大工程了。。得在 BPF 里构建一个包。。
@s82kd92l
MIPS 不支持,看上面关于 OpenWrt 的回复 https://www.v2ex.com/t/1029886#r_14545955
@maybeonly
> 对于碎片,考虑发 icmpv6 type2 或者 icmp type3 回去?不确定能起多大作用。

这个只有改了包的大小超过 MTU 时需要发回去,比如 Cilium 的 NAT64 对超过 MTU 的包就是这样的,但 einat 没改理应不需要啊。。 为什么内核没有拆包我就不知道了

> ctrl+c 掉程序没有清理 tc 钩子,下次重启进程得手工删 tc

正常情况下 ctrl + c 是会清 bpf 程序再退出的,可以`bpftool prog` 看一下有没有 `egress_snat` 和 `ingress_rev_snat`, 但 qdisc clsact 确实没删但也没什么大问题(主要是懒得查 qdisc 占用情况了,也不能全部删掉。。)
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1664 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 15ms · UTC 16:25 · PVG 00:25 · LAX 08:25 · JFK 11:25
Developed with CodeLauncher
♥ Do have faith in what you're doing.