maybeonly 最近的时间轴更新
maybeonly

maybeonly

V2EX 第 193338 号会员,加入于 2016-09-25 08:10:24 +08:00
今日活跃度排名 1587
maybeonly 最近回复了
1 天前
回复了 maybeonly 创建的主题 宽带症候群 跟风贴自家软路由实现
@povsister
简要流程:

DNS on query -> 匹配隧道? -> YES: 丢给对应的隧道 / NO: 丢回系统递归

DNS on reply -> 匹配隧道? -> NO: 直接返回
... -> YES: 必要时查 ipv4/v6 映射表 -> 记录 DNS 结果地址、DNS 请求的源地址( edns 或者源 IP ),设置 TTL (比如不超过 5min ) -> 设置路由规则(源,目的,超时时间,隧道 ID 。其中超时=DNS 超时+允许的宽限时间) -> 返回记录

on PACKET -> 匹配路由规则(超时的这里就匹不到了)? -> YES: 修改目的 MAC 为隧道对应的 MAC / NO: 修改目的 MAC 为 OWGW 的 MAC -> rawsocket 发出

系统的路由表实现源*目的匹配各种不方便,更别说超时了。这也是自己搓二进制的主要原因。
1 天前
回复了 maybeonly 创建的主题 宽带症候群 跟风贴自家软路由实现
@Jirajine
> 不过用户态一个函数能实现的功能在系统网络栈配合路由和规则做,正常来说最大的优势就是性能了。模块之间如果需要交流,那说明是耦合到一起的。
后面的 “交流” 指的是数据流转,比如后面挂了 3 个不同的梯子,那么数据总是得从调度进程到梯子进程的……不管整合不整合梯子都是 clash 既视感。
另一方面,在现实中,运维水平也是不得不考虑的……
所以这里的取舍是,选择 netns 集群便于维护的特点而放弃极致性能(其实也没多差)。

> 如果你的 vpn 入站需要路由除访问内部资源以外的流量,那是不是因为你这一套做了太多该在客户端做的,从而不够 portable 。
不是很确定你要表述什么。客户端连上入站 vpn 什么都能访问,就和连上 wifi 几乎一样(除了 2 层)。vpn 入站模块只需要路由器给转发特定端口就行。
现在的客户端可不是以前了,电脑怎么都好说,手机、平板、电视,IoT……还是设计哲学问题,我不想在客户端放复杂的东西,能连个不同的 ssid 就是极限了。
1 天前
回复了 maybeonly 创建的主题 宽带症候群 跟风贴自家软路由实现
@Jirajine
> 你用一堆 netns 组了个虚拟集群,数据包发过来发过去,这开销一点不比用户态低啊。
没错,参见设计目标第二条:并不是追求极致性能。
p.s. 即使是这样的耦合,即使效率可能比不上用户态,调试和修改起来也会比单个用户态程序容易太多了。以及用户态同样要面对和其他模块(比如 vpn )交流的问题。

> 比如任何情况下任意客户端的任意 dns 请求和后续实际请求能够路由到相同的外部接口。
对于 dnsroute 列表内的域名来说,强保证同一个出口。
对于其他的,经过权衡利弊,故障切换比较重要,不保证同一个出口。否则对于 TTL 比较长的普通域名,靠前的隧道 up/down 会很难受。

> 路由器只要做好两件事:把不同客户端的出战连接路由到不同外部接口;追踪入站连接,回程路由到来源的接口。
这正是三个核心 GW+重要模块 DNSROUTE 的功能。如果需要依赖 DNS 做路由分流,那么这里混进一点 DNS 不可避免。
而且这里设计 DNSROUTE 只是“重要”,也就是说,必要的时候可以从核心模块上拆下来(当然功能也就没了)。

> 其他的你想用什么隧道、起什么服务、vpn 入站、通过 ip/mac/vlan 等方式识别客户端等等都是全部解耦、互不影响的。
这个确实全部解耦了。识别客户端在 INGW 中的 ingw.d 模块(还特别出现了 NET-XC ,就是为了把 VPN 入站的也拉过来),服务是单独的可拆卸模块(除了几个 DNS 是耦合度较高的),VPN 入站也是单独的模块(现在实现了 openvpn 和 wg 两个完全独立的模块),VPN 出站每个 VPN 抽象为一个隧道和其他 VPN 互不影响(串接除外)。

> 其他更复杂需求客户端自己做更合适。
算了,各种策略还是从路由器上下发吧,这是设计哲学的问题。ingw.d 很大程度上也是为了干这个。
1 天前
回复了 maybeonly 创建的主题 宽带症候群 跟风贴自家软路由实现
@povsister 这个问题思考过,并没有那么疼。
首先,基本上没可能解析到同一个 anycast ip 。大厂的 cdn ip 也是专用的。好吧,假设他真的解析到同一个 anycast ip 了。
其次,比较现实的情况是,使用了两个不同的 dns 解锁机,分别解析到商家的美国解锁机和日本解锁机上,那这俩 ip 显然也不一样。
第三,更现实的情况,真实场景下,同一个用户/客户端几乎不可能同时用 nf 和 dmm 。所以实现的时候有考虑源 ip*目的 ip 做匹配,假如电视上看 nf ,手机上玩 dmm ,那是一点都不会出问题的。就算在同一台机器上切换,只要别来回切,后续解析成功的规则会覆盖前一个,而之前建立的 tcp/udp 连接则不会断——这里 dns 解析的结果是缓存在递归里的,而 dnsroute 会把 ttl 改小让用户不要太久不请求。
整体上来讲,不可能完全避免这类问题,但是现在的实现,已经可以让这类问题发生的几率足够小了。

至于嗅探,还是算了吧,当时也考虑到 ech (当时还是 esni ),从开头就没有打算做。
1 天前
回复了 maybeonly 创建的主题 宽带症候群 跟风贴自家软路由实现
@MeteorVIP
网速慢会被老婆骂,广告过滤列表过滤不掉广告会被老婆骂,dns 解锁不灵会被老婆骂,老婆在外边连不上回家的 vpn 也会骂。
好在这些年了除了在有计划的调整之外,只有硬件故障和电力故障能搞坏它。
1 天前
回复了 maybeonly 创建的主题 宽带症候群 跟风贴自家软路由实现
@Jirajine
耦合并没有那么深,重要性也是分层的。
比如在外边,开着公网 ssh 的话(实际上就是),只要三个核心的模块( INGW ,OWGW ,RTGW )活着就能连回去。当然对应的 WAN 得正常。
然后一些是重要模块,比如 DNSIW ,DNSOW ,RECURSIVE ,DNSROUTE ,这些正常工作的话里边可以正常上网。
不仅仅各种隧道是模块化的,连入 VPN 、ADBLOCK 这些也都是模块化的。
再有就是,分 netns 是为了能有更强的扩展能力,也是为了每个 netns 里规则相对清晰,更是可以实现路由和 nat 分离,这一点在 dnsroute 里有集中的体现。
核心模块搭起来的是一个框架,然后就可以往上附着各种模块。
还有一些模块不是以 netns 的形式出现的,比如存在一个 ingw.d 模块,可以设置某些客户端( ip/ip6/mac )是不是使用去广告 dns ,是只有常规端口过梯还是所有端口过梯,etc.。更别说还有隧道管理器、流量监控之类的东西,纯属外围模块。又比如如果有需求,可以很轻松地给一个或多个 WAN 添加 nat1 模块而不影响整体,之类的。

@jsq2627
自建递归是一种权衡的选择。
主要目的是为了给没有在名单里的域名兜底,也从根本上杜绝 dns 泄漏之类的问题。
很久很久以前是用黑名单解析境外个别网站的,后来经常因为名单维护不及时而有时候连不上很恼火,逐渐改成了现在这个样子。
实际上没有想象的那么慢,例如解析 www.google.com实际上.com 是几乎肯定被缓存的,只需要 2rtt 就能解到。
复杂一点的,比如 www.163.com ,要 cname 两次,需要 7rtt 才能解析出来(当然 163 显然是在墙内白名单里的)
如果觉得有必要可以用名单手工指向墙内或墙外。

@povsister
dns 分流这一点还是相信自己的创造力的。
现代专业梯子分流都是基于全用户态实现,dae 将无需代理的部分绕过了梯子用户态。
实际上这些软件,特别是 dae 之前的软件,设计场景都是单机使用,如果用在软路由上,就不可避免地会经常出现“为什么不过墙的网络也受影响”“啊,我改个梯子把整个网络搞坏了”“所以还是旁路由好”之类的情况。dae 一定程度上解决了第一个问题。
这边的实现,将调度模块与隧道模块分离,很大程度上解决了第二个问题。
通过自动维护的映射表( LAN 通过 ip nei ,openvpn 通过 status.log ,wg 通过 allowedips ),解决 v4/v6 映射的问题。同时,考虑隧道可用性,匹配最佳隧道,对首包用用户态通过 mac 地址选择下一跳,然后根据回包 mac 地址匹配 conntrack ,并且在后续甩掉用户态程序全依赖 conntrack ,极大程度保证了首包以外的性能,而且 dnsroute 本身负载很低,也解决了第一个问题——这一段肯定够写个专利了。
c 艹虽然确实比较艹,但是真的是很好用的。应该说最早还在用 dd-wrt 的时候,就有 c++写的一些其他模块在用了,所以说形成路径依赖了吧,那时候肯定是 2012 年之前的事情了。
@eh5
> “和很多东西冲突”

也曾考虑过这方面的问题,在真实部署中会很依赖端口选择(配置)
确实,代码本身不会利用自己已经用过的 snat 后的源端口,但是如果这些端口和其他 dnat 规则冲突,抑或被其他程序占用呢?运营商的 cgnat 没有这个问题,毕竟人家的 ip 就是专供 nat 用的。家用的话,可能不得不特别小心 dnat 的选择,以及用 ip_local_port_range 隔离了。然后发现 dnat 搭配的 hairpin 怎么办? emmmm 。。。

当时自己考虑的结果是,不得不和 conntrack 做某种程度的“交易”才能解决这个问题。
交给 conntrack 选 snat 后的源端口就没这个问题,只要识别到这个内部 ip:port 和外部 ip:port 的组合,后续不走 conntrack 按照 fullcone 的实现就好了。不仅能指定源端口,也可以实现比如针对某个 ip 实现 fullcone ,etc 。

同时被解决的另一个问题是,什么都没配置的话可以继续走系统的 conntrack ,只有配置了命中了正确的端口范围才能 fullcone 。在实践上也会是相对比较安全的。

然而咱终究是懒得 1b ,对勤奋的楼主表示深深的敬意。
@eh5
> 这个只有改了包的大小超过 MTU 时需要发回去,比如 Cilium 的 NAT64 对超过 MTU 的包就是这样的,但 einat 没改理应不需要啊。。 为什么内核没有拆包我就不知道了
理论上吓一跳链路比包“窄”就需要。虽然对于 ipv4 ,我是没见过哪个路由器是不分片而回 icmp 的(除非设置了 df )

> 正常情况下 ctrl + c 是会清 bpf 程序再退出的,可以`bpftool prog` 看一下有没有 `egress_snat` 和 `ingress_rev_snat`, 但 qdisc clsact 确实没删但也没什么大问题(主要是懒得查 qdisc 占用情况了,也不能全部删掉。。)
没有了,只是再启动会报个 warning ,并不影响正常工作的。
WARN einat: libbpf: Kernel error message: Exclusivity flag on, cannot modify
@eh5
1. 既然想着共存了,改 conntrack 确实不是好事情,不过似乎可以(部分)绕过 conntrack ?
当时考虑的是在入口和出口分别捕包,然后在出口处发现符合条件的报文后反查刚刚从入口抓过来的数据包,可能用到的匹配条件有:protocol+dst ip & port/id, length, 应该还有 ip 报文的 id 。
抓到该端口相关的东西后续由 ebpf 完成 nat ,不再经过内核。
2. 对于碎片,考虑发 icmpv6 type2 或者 icmp type3 回去?不确定能起多大作用。

由于我太懒了,以上全部都停留在设想,具体能实现到什么程度,在真实网络环境中运行咋样,以及对性能的影响,也只能说停留在设想中了。。。
p.s. ctrl+c 掉程序没有清理 tc 钩子,下次重启进程得手工删 tc 。。。

再次感谢楼主。
很多年前在本站某网友的影响下设置为 254 ,就渐渐成为习惯了
直到今天我家内网有 7 个/24 了还是这样
曾经某一天有了 v6 ,突然发现,这 192.168.0.254 是网关,那 v6 网关是多少?
1. 用 2001:db8::fe ?嗯,有点奇怪,但是其实是问题最小的。(实际上在用这个)
2. 用 2001:db8::1 ?啊,那 192.168.0.1 怎么办?
3. 用 2001:db8::ffff:ffff:ffff:fffe ?*,输入这种地址要死了。
4. 肯定有人说,用 fe80::1 呀?这个,路由没问题,但是如果需要连接到网关呢?(实际上也配置了 fe80::1 ,并不矛盾)
你看,用.1 就这些鸟问题。
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5433 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 15ms · UTC 07:04 · PVG 15:04 · LAX 00:04 · JFK 03:04
Developed with CodeLauncher
♥ Do have faith in what you're doing.