V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
maybeonly
V2EX  ›  宽带症候群

跟风贴自家软路由实现

  •  3
     
  •   maybeonly · 193 天前 · 5606 次点击
    这是一个创建于 193 天前的主题,其中的信息可能已经有所发展或是发生改变。

    设计目标

    • 为了玩得开心。
    • 并不是追求极致性能。实际上用 n100 跑,常年 CPU idle 90%+。
    • 模块化,面向数据包和连接,全真 IP
    • 结构复杂但是条理清晰,拓展性好。轻松适配各种形式的 VPN 。

    设计总则

    • 基础 OS 使用 Rocky linux 9 。为什么使用 RH 系,问就是历史原因。
    • 从 DD-WRT 上迁移出来大概是 2016 年,逐步发展成现在的样子。
    • 使用 netns 创建各自相对独立的网络命名空间
    • 各 netns 分别分配功能、配置 ip ,用虚拟网卡、虚拟网桥互联
    • 虚拟网卡(直接接入 LAN 的除外)统一分配静态 MAC 地址,由 IP 地址生成。MAC 匹配被大量应用
    • 静态路由宣告,因为都在同一台物理设备,直接使用脚本在必要的 netns 内执行 ip route add 。
    • 总体使用 bash 编写,部分模块使用 c++
      • 最初是感觉容易移植来着
      • 实际上的好处是脚本部分容易修改
      • ~-Golang ?是不错,但是咱不熟。-~ 问就是路径依赖
      • 没有 gui ,懒得写,也没必要。有一些 cli 工具倒是。
      • 目前使用了 7 个/24 的 ipv4 ,及 4 个/64 的 ipv6 ,尽管并不是所有的 ip 段都是必须的
      • 其中,IX 、TUN 公用一个/64 ,因为这些业务都可能直接和外界连接,选择了节约 IP 段
      • LAN 有一个用于 SLAAC 的/64
      • 另留有一个/64 用于 LAN 的静态 v6 的 SNPT (历史遗留设计,但是这样比较容易配置 IP )
      • 一个独立的/64 ,用于不同于 SLAAC 的 v6 公网地址访问外部时使用(历史遗留设计;实际上可以不使用)

    具体实现

    • NET-IX ,虚拟交换
      • 软路由的核心,使用一段内网 v4 (/24 ),并分配一段内网 ipv6 (/68 )
      • 提供的主要功能包括,调度(故障自动处理、分流)及服务( DNS ,AC ,etc )
      • 其中的主要功能模块:
        • INGW ,内网接入网关,同时配置 LAN 侧网关 IP
          • 内置各种策略,以针对不同的接入设备应用不同的实际 DNS 等
          • 可以支持基于 IP ( v4 ,v6 )地址配置策略,对于直接接入的设备,也可以通过 MAC 地址配置策略
          • 对于来自 VPN 的流量,采用 XC 网段互联,将流量导入到 INGW 应用策略
          • 对于不同的策略,匹配不同的路由表。DNS 服务的 IP 使用 VIP:对于设置需要过墙的设备,转发到 DNSOW ;其他转发到 DNSIW
          • 类似的,还可以配置其他策略,如存在 adblock 策略,可配置 DNS 的 VIP 到带广告过滤的 DNS
        • DNSIW ,普通 DNS ,dnsmasq 转发运营商 DNS
          • 对于多条宽带,手工配置了两个运营商的 DNS
        • DNSOW ,过墙 DNS+白名单一次筛选,dnsmasq 设置白名单转发前述 DNSIW ,其他转发 RECURSIVE
          • 对于需要 DNS 分流的,全部转给 DNSROUTE/或者全部转给 DNSROUTE
          • 使用 EDNS 带内网源 IP
        • OWGW ,隧道入口,对于白名单内的 IP 转发至 RTGW 。更多详情见 TUN 部分。
          • 使用了国内 IP 列表
        • BLACKHOLE ,黑洞,吃掉没用的东西并返回 icmp/icmp6
        • RECURSIVE ,递归解析,单栈,bind 实现
          • 为什么没用 mosdns ?问就是历史路径依赖,做到这个大概是 2018 年
          • 当然,换成其他递归也没问题
        • DNSROUTE ,dns 分流/策略分流,c++自行开发,后续有详细说明
        • RTGW ,IX 网段的出口网关,连接到 WAN 。详情见 WAN 部分。
      • 其他次要功能模块
        • AC ,AC 控制器,用于控制 AP
        • ADBLOCK ,去广告 dns ,去广告并转发 DNSOW
          • 使用 EDNS 带内网源 IP
      • 基本分流原理:DNS 白名单+IP 白名单
        • 对于 DNS 白名单内的域名,转发到 DNSIN ,本质上是转发 ISP 的解析的。
        • 对于 IP 白名单内的 IP ,当然是直接走运营商出口(交给 RTGW )出去
        • 对于 DNS 白名单外的域名,通过 RECURSIVE 解析
        • RECURSIVE 出来的流量会根据 IP 白名单进行分流
        • 因此,(绝大多数情况下)如果解析的是境内网站,那么最后一次查询权威会访问境内的权威服务器,因此他的权威看到的你的地址是宽带出口,会正常返回境内并且合理的 IP 地址。
        • 如果解析的是境外网站,那么最后一次权威查询的是境外服务器,会看到境外出口 IP 的地址,返回正常的境外 IP 。
        • 有的境内网站也有境外权威?是的,但是通常境内权威会快得多。对于屡教不改的个别网站(如果有的话),手工补充进 DNS 白名单就是了。
    • NET-LAN ,普通接入
      • 分配独立的内网 v4 (/24 )和 v6 (/64 ),使用 dnsmasq 和 radvd 管理
      • 考虑到一些设备需要拿到真实 v6 ,目前采用实际的 v6 分配,所以每次 v6 地址变化需要更新地址以及相应的路由表
        • 也可以直接分配 fd 开头的 IP ,反正可以 SNPT
        • SNPT ? nft 您的 cli 支持吗?还是继续用 iptables 吧
      • 无 TAG ,直接接交换机,墙上的网口都接入这个网段
      • 通过 INGW 与 NET-IX 互联并实现策略
    • NET-WAN ,宽带接入
      • 只有链路本地的 v6 ,使用 fe80::/10 可以转发数据包即可
      • 实现了 PPPoE 、DHCP-client 等几个常见的接入,还有独立的 IPTV 接入,通过 udpxy 转发实时节目
        • 因为我拿到运营商送的机顶盒的时候,就已经那样看了好多年了
      • RTGW ,连接到 NET-IX
        • 实现了 connmark ,用于解决多宽带连入连接的问题
        • 还依赖了 ctdir 来区分连入还是连出连接
        • 对不同的宽带,使用静态路由表+子路由表打底默认路由的形式
          • 网速叠加?算了算了。真的用得到极限网速的机会非常少。
          • 而且……这样搞势必跨运营商,效果真的好吗?
          • 真想做的话会考虑在 DNSIW 那里。
      • 对于来自 WAN 侧的数据报文,如果对应的连接没有 connmark ,则根据来源 MAC 地址记录 connmark 供后续包使用
      • 对于每条宽带,使用两个 NS ,分别是 ISP 和 PROXY:
        • ISP ,装有对应的物理网口,设置默认路由为运营商侧
        • ISP 绑定运营商的 v4 ;对于 v6 ,这边 pd 到/60 的段,加黑洞路由,然后将用得到的地址(一个/62 )指向 RTGW
        • ISP 进行 SNAT ,对于 v6 ,进行 SNPT ,将内网用到的 v6 地址(包括 LAN 用到的那一段“公网”IPv6 地址) SNPT 成合法的 v6 ;例如,分配了来自联通宽带的 IPv6 给内网,那么需要从移动出去的时候就会被 SNPT 成移动的 IPv6 。
        • 对于 PROXY ,包含通往 TUN 网段的虚拟网口,将所有流量都转发给对应的 ISP 。用于解决 TUN 中需要指定使用某条线路的问题。这里没有反向路由,反向走普通 RTGW 回去。
        • 如果有一只海外小鸡,这下就可以变成( v4+v6 )*(移动+联通)一共 4 只了!结合负载均衡或者健康检查一下还能有稳定的出口 IP 。
    • NET-TUN ,模块化连出 VPN
      • 使用一个独立的内网 v4 网段(/24 )、一段内网 v6 网段(/68 )
      • 额外接入 OWGW 、DNSROUTE 连接到 NET-IX ,以及 RTGW 连接到 NET-IX 和 NET-WAN ;还有接入 PROXY 连接到 NET-WAN 用于线路选择
      • 对于每一条隧道,均包含两个 netns:TN 和 FW
        • 当前的/24 可以配置 120+个隧道,实际上配置了 100 个
        • TN 用于接受转发过来的数据包,通常是 OWGW 或 DNSROUTE 转发过来的,将这个数据包通过各种协议( tproxy ,tun ,etc )丢进某个隧道或者丢给 FW
        • TN 的默认路由指向 FW 或隧道,视协议而定
        • TN 和 FW 之间现在有独立的一对 veth 互联,各自设置 noprefixroute ,未来有将 TN 和 FW 分开到两个不同网段的考虑
        • FW 默认路由指向 RTGW (自动)或某个 PROXY (手工指定出口)
      • 现在的典型配置是,隧道程序在 FW 运行,个别在隧道上运行的 OpenVPN 等放在 TUN
      • VPN 出口调度:
        • 在 OWGW 中操作
        • 使用类似 RTGW 的 connmark 策略调度各连接
        • 对于有 connmark 的数据包,按照 connmark 调度
        • 除了来自 RECURSIVE 的请求,如果没有 connmark ,那就请他去 DNSROUTE 走一圈,除非他刚刚从那边来(看来源 MAC )
        • 如果仍然没有 connmark ,匹配路由规则,转发给编号最小的有效隧道
          • 在此 NS 中,存在大量的 ip rule 和多张路由表。好在对应每个隧道的 rule 相对简单,就是指向对应的路由表;对应的路由表也简单,就一条,默认指向某 TUN 的 TN
        • 如果收到来自任何隧道的数据包,记录 connmark 供后续使用(还是看来源 MAC )
      • 自动化健康检查
        • 有额外的脚本去不断检测各隧道
        • 对于质量不佳的隧道删除对应的路由规则但是保留路由表
        • 这样后续新建的链接就不再会进入那些隧道了
          • 但是对于已经打 mark 的连接仍然会保持
          • 避免了切换隧道时大量连接断开,特别是在一个优先级高的隧道变成可用时
      • DNSROUTE 目前使用自行开发的 C++程序实现:
        • 匹配 DNS 解析请求报文
        • 如果匹配策略,交给对应的 TUN 解析;对于返回的 A/AAAA 记录,记录源目的 ip
          • 这里有个小问题。如果只有部分域名会经过 DNSROUTE 的话,需要拍平 CNAME 。或者就让所有域名都经过 DNSROUTE ,只需修改 TTL 。都实现了,上线的是前者。
        • 对于转发过来的数据报文,使用类似 LVS-DR 的方法,通过 rawsocket 贴上对应的目的 MAC 地址发给对应的隧道
        • 效率?那当然是比较差了,甚至我是没本事把他塞进 ebpf 。不过好在 OWGW 只会把每个连接的第一个/前几个报文转发过来,因为一旦有来自隧道的报文回到 OWGW ,连接就有了 connmark ,后续报文就不再依赖 DNSROUTE 了
        • v6 怎么办?凉拌,做映射表,或者在这部分把 v6 关掉。还记得刚才的黑洞吗? OWGW 上把 v6 的默认路由丢进去就没烦恼了
        • 现在已知一个 bug ,没能正确处理碎片包,好在几乎没有影响,后续会修改
    • NET-XC ,内部互联,将连入 VPN 导入 INGW 并应用策略(只能应用三层策略)
    • VPN ,拨入 VPN ,适当实现即可,用 openvpn 或者 wireguard 或者两个都用都没问题
    • NET-IoT ,物联网,使用 VLAN ,在 AP 上开不同的 SSID 。
      • 因为只需要访问境内网络所以从 NET-IX 直接接入默认路由指向 RTGW 即可。
      • 这是历史遗留设计。如果现在设计的话也会走 NET-XC 了,能实现但是懒得改。

    为什么说玩得开心呢

    • 当然是在外边也可以进行很多修改
      • 有了新的小鸡/要调整某些隧道,只需新增一个 TUN 配置文件,选一个空着的 TUN ID ,放到相应目录里,然后 addtunnel ${TUNID}( cli 工具)就好了。删除的话也只需要 removetunnel ${TUNID}。不会影响整体上网,也不影响其他隧道。
      • 调整健康检查也是。健康检查程序分成两部分,一部分负责检查,将觉得可以用的 TUNID 写到一个目录中;另一个用 inotify 或者什么都好听着那个目录,去操作 OWGW 中对应的规则。显然,前一部分也是可以在外边修改甚至停下来的。
      • 多 WAN 支持,甚至可以在外边改一个暂时不用的 WAN 。
      • 只有核心的几个模块是必须在本地改的,好在他们现在也不需要什么改动。
    • 还有就是,复杂但是有很大的扩展空间。
      • 在大约 2020 年上线 DNSROUTE ,用了源+目的双 IP 匹配
      • 后来给每个 TUN 都加了属性,给 DNSROUTE 新增了功能。就是对于来源于某个 IP/CIDR 的,发给带有特定属性的 TUN 。
        • 比如我有一只美国小鸡,通过多条 TUN 连接,都含有某个属性
        • 然后可以设置一条规则给 DNSROUTE ,让他对来自某个特定 IP 段,比如 192.168.5.0/24 的,转发给这个属性的 TUN
        • 然后我就可以开一个 SSID ,比如叫“美国直通车”,通过 vlan 分 192.168.5.0/24 的 IP ,手机连上这个 wifi 就可以直通美国了
      • 这么玩还可以隧道串接
        • 比如某个小鸡的出口买了 DNS 解锁
        • 那么就给这个小鸡出口的相关 TUN 都带上某个属性
        • 起一个新的 TUN ,叫做“dns 解锁”。其 FW 出口不是通常的指向 RTGW 或者 PROXY ,而是指向 OWGW (需要设置 SNAT )
        • 但是同时给 DNSROUTE 设置,来自于这个 IP 的,走某个属性的出口
        • 最后效果就是,即使某条线路不好用了/某个宽带断了,只要还能到那个 IP ,就能维持解锁
    53 条回复    2024-04-26 14:57:04 +08:00
    yuanfa
        1
    yuanfa  
       193 天前
    大佬!
    tediorelee
        2
    tediorelee  
       193 天前
    挖趣这也太复杂了
    StinkyTofus
        3
    StinkyTofus  
       193 天前
    太长了, 没看完
    Mrealy
        4
    Mrealy  
       193 天前
    大佬,这么多字我也看完了。
    yyysuo
        5
    yyysuo  
       193 天前
    这就有点高端了。
    x86
        6
    x86  
       193 天前
    今天刷小红书看一个评论的帖子分体水冷都上了,e5 做的软路由
    YGBlvcAK
        7
    YGBlvcAK  
       193 天前 via Android
    大佬牛逼,我只是用 debian 玩旁路由,你这是整了个主路由!
    AEnjoyable
        8
    AEnjoyable  
       193 天前
    我以为的软路由:op ( ROS,ikuai )+ap+交换机

    佬的软路由:自己实现?+ap
    JensenQian
        9
    JensenQian  
       193 天前 via Android
    快进到家里上机柜
    rrfeng
        10
    rrfeng  
       193 天前   ❤️ 1
    这不得天天玩路由器,原来路由器=PS 是有道理的
    Jeremial
        11
    Jeremial  
       193 天前   ❤️ 1
    每个字都认识, 但是加一起不认识了
    hahaha121
        12
    hahaha121  
       192 天前
    好家伙好家伙
    NeoChen
        13
    NeoChen  
       192 天前 via iPhone   ❤️ 3
    你这跟谁的风,没人跟得上你的风
    Jirajine
        14
    Jirajine  
       192 天前
    感觉你这些 netns 之间高度耦合,那分这些“模块”意义在哪里。大部分需求单机策略路由+netfilter+广泛使用的用户态程序都能解决,再不行起个 tun/tap 全发到用户态自己想怎么搓就怎么搓。
    自己跑 recursive 解析器意义也不大。
    jsq2627
        15
    jsq2627  
       192 天前
    自建 recursive ,对于没有缓存的域名那岂不是慢上天了?
    apiman
        16
    apiman  
       192 天前
    都没勇气全部看完,每一个字都认识,组合在一起不知道大佬在说啥
    zhuantouer
        17
    zhuantouer  
       192 天前 via Android
    高端玩家,好奇 op 啥工作背景,怎么这么专业,纯爱好那就 orz 了
    povsister
        18
    povsister  
       192 天前 via iPhone
    分流思路和现在各种代理 core 差不多,而且在 dns route 上也摒弃了 FakeDNS 的路子好评。
    但是,什么?你用 C++写的?还搓了这么多轮子?这是真大佬惹不起惹不起。
    kkk9
        19
    kkk9  
       192 天前
    op 配置好可以打包镜像封装 OS 开卖,就叫 ChinaRouteOS
    s4nd
        20
    s4nd  
       192 天前
    你有勇气写这么多,我却没有勇气看完
    aptupdate
        21
    aptupdate  
       192 天前 via iPhone
    怎么和我玩的软路由不太一样。。。
    dodakt
        22
    dodakt  
       192 天前
    我看完了 也看懵了 没有一个字是我不认识的 没有一段是我能看懂的
    MeteorVIP
        23
    MeteorVIP  
       192 天前
    你这确实很高端喔,虽然我看不懂,
    但是我就想问,掉线,网速慢会被老婆骂吗?
    maybeonly
        24
    maybeonly  
    OP
       192 天前
    @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 年之前的事情了。
    maybeonly
        25
    maybeonly  
    OP
       192 天前
    @MeteorVIP
    网速慢会被老婆骂,广告过滤列表过滤不掉广告会被老婆骂,dns 解锁不灵会被老婆骂,老婆在外边连不上回家的 vpn 也会骂。
    好在这些年了除了在有计划的调整之外,只有硬件故障和电力故障能搞坏它。
    yyysuo
        26
    yyysuo  
       192 天前   ❤️ 2
    @maybeonly #25 擦,军功章上有你的一半也有贵夫人的一半啊。
    KazeW
        27
    KazeW  
       192 天前
    这 这才是软路由?
    佬这不是跟风,是平地起狂风
    MeteorVIP
        28
    MeteorVIP  
       192 天前   ❤️ 1
    @maybeonly #25 挨骂我就舒心了,哈哈哈
    zylll520
        29
    zylll520  
       192 天前
    大佬独领风骚👍
    shunia
        30
    shunia  
       192 天前
    不对,这不是我要上的车!
    defunct9
        31
    defunct9  
       192 天前
    大佬买个 AS 域,折腾 BGP 吧
    hi2hi
        32
    hi2hi  
       192 天前
    大佬!太强了
    povsister
        33
    povsister  
       192 天前
    @maybeonly #24
    DNS 分流实现思路和我一样,都是“不过墙的网络完全不走梯子”,但是我选了继续完善旁路由方案,对于 conntrack 部分我还是依赖主路由的路由表和防火墙的。给你这个全手搓的 DNS 分流跪了 Orz

    不过我的帖子里,@mohumohu 提出了一个很有意思的问题:
    有些网站会有区域限制,全真 IP 的情况下,如果两个网站解析到同一个 anycast IP ,而且域名嗅探失败的情况下,基于 conntrack 这一套 L3 的分流,如何正确选择代理隧道?
    举个例子:假设 Netflix 套了 Cloudflare 的 CDN ,解锁 NF 需要美国 IP 。假设 DMM 也套了 CF 的 CDN ,解锁 DMM 需要日本 IP 。但因为 CF 的全球 CDN 网络,这两个站解析出的 IP 地址都是同一个 anycast 地址。

    代理实现越靠下层,则会损失越多偏上层的信息。
    我思来想去感觉很无解,随着 http3 和加密 SNI 普及,IP 数据包中的域名嗅探会变得越来越难,那么全真 IP 下的域名分流机制可能会始终面临这个痛点。
    maybeonly
        34
    maybeonly  
    OP
       192 天前
    @povsister 这个问题思考过,并没有那么疼。
    首先,基本上没可能解析到同一个 anycast ip 。大厂的 cdn ip 也是专用的。好吧,假设他真的解析到同一个 anycast ip 了。
    其次,比较现实的情况是,使用了两个不同的 dns 解锁机,分别解析到商家的美国解锁机和日本解锁机上,那这俩 ip 显然也不一样。
    第三,更现实的情况,真实场景下,同一个用户/客户端几乎不可能同时用 nf 和 dmm 。所以实现的时候有考虑源 ip*目的 ip 做匹配,假如电视上看 nf ,手机上玩 dmm ,那是一点都不会出问题的。就算在同一台机器上切换,只要别来回切,后续解析成功的规则会覆盖前一个,而之前建立的 tcp/udp 连接则不会断——这里 dns 解析的结果是缓存在递归里的,而 dnsroute 会把 ttl 改小让用户不要太久不请求。
    整体上来讲,不可能完全避免这类问题,但是现在的实现,已经可以让这类问题发生的几率足够小了。

    至于嗅探,还是算了吧,当时也考虑到 ech (当时还是 esni ),从开头就没有打算做。
    Jirajine
        35
    Jirajine  
       192 天前
    @maybeonly #24 你用一堆 netns 组了个虚拟集群,数据包发过来发过去,这开销一点不比用户态低啊。
    复杂度这么高,能保证 correctness 吗,比如任何情况下任意客户端的任意 dns 请求和后续实际请求能够路由到相同的外部接口。
    那些代理软件确实不适合在路由器上使用,它们提供的功能根本就不是路由器需要的。路由器只要做好两件事:把不同客户端的出战连接路由到不同外部接口;追踪入站连接,回程路由到来源的接口。其他的你想用什么隧道、起什么服务、vpn 入站、通过 ip/mac/vlan 等方式识别客户端等等都是全部解耦、互不影响的。其他更复杂需求客户端自己做更合适。
    Jirajine
        36
    Jirajine  
       192 天前
    @povsister #33 因为你在尝试在错误的 layer 解决问题。开两个 firefox container ,一个走美国代理打开 netflix 、一个走日本代理打开 dmm 不就完事了,什么规则、路由、dns 都不需要。
    maybeonly
        37
    maybeonly  
    OP
       192 天前
    @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 很大程度上也是为了干这个。
    huangya
        38
    huangya  
       192 天前
    这些配置,有些可不可以整理一下放 github ?
    Jirajine
        39
    Jirajine  
       192 天前
    @maybeonly #37
    > 调试修改起来比单个用户态程序容易太多
    好吧,你是写 Cpp 的,能够理解😂
    不过用户态一个函数能实现的功能在系统网络栈配合路由和规则做,正常来说最大的优势就是性能了。模块之间如果需要交流,那说明是耦合到一起的。

    如果你的 vpn 入站需要路由除访问内部资源以外的流量,那是不是因为你这一套做了太多该在客户端做的,从而不够 portable 。
    maybeonly
        40
    maybeonly  
    OP
       192 天前
    @Jirajine
    > 不过用户态一个函数能实现的功能在系统网络栈配合路由和规则做,正常来说最大的优势就是性能了。模块之间如果需要交流,那说明是耦合到一起的。
    后面的 “交流” 指的是数据流转,比如后面挂了 3 个不同的梯子,那么数据总是得从调度进程到梯子进程的……不管整合不整合梯子都是 clash 既视感。
    另一方面,在现实中,运维水平也是不得不考虑的……
    所以这里的取舍是,选择 netns 集群便于维护的特点而放弃极致性能(其实也没多差)。

    > 如果你的 vpn 入站需要路由除访问内部资源以外的流量,那是不是因为你这一套做了太多该在客户端做的,从而不够 portable 。
    不是很确定你要表述什么。客户端连上入站 vpn 什么都能访问,就和连上 wifi 几乎一样(除了 2 层)。vpn 入站模块只需要路由器给转发特定端口就行。
    现在的客户端可不是以前了,电脑怎么都好说,手机、平板、电视,IoT……还是设计哲学问题,我不想在客户端放复杂的东西,能连个不同的 ssid 就是极限了。
    povsister
        41
    povsister  
       192 天前
    @Jirajine #36
    不能算在错误的 layer 解决问题吧,只能说是:把解决的问题的阶段下沉,对内网终端设备隐形。
    然而把方案下沉,其实哲学意义上,就意味着解决问题时能获取的有效信息会减少。
    这和运营商选择把反诈插件放在用户侧光猫里是一样的道理,只有足够贴近用户,才能提高反诈预警的准确性(只是举个例子哈,不对反诈这个东西做任何评价。

    说到底,我和 OP 的哲学是一样的,能在网关解决的问题绝不在客户端上解决。
    Jirajine
        42
    Jirajine  
       192 天前
    @maybeonly #40 很多事情在客户端做更适合也更简单,比如把不同的 app 路由到不同的接口,你在网关就没有足够的信息来做;再比如 dnsroute ,不如浏览器自己分配不同的 tab 走不同的代理。
    至于电视、iot 设备,这些直接在网关全局路由到某个接口就行。
    在外面上网还要走 vpn 连回家的话,开销就不是一般的了。
    kylix
        43
    kylix  
       192 天前
    好详细,先收藏,再慢慢看~~~
    povsister
        44
    povsister  
       192 天前
    @maybeonly #34 感谢分享思路!
    这个问题我个人猜测应该是不会那么痛,但是明显还是大佬思考更细致。

    我赞同你对现实情况的考量,只要路由选择上添加 srcIP - dstIP 做匹配,把不同终端的路由选择分割开,就能极大缓解这一问题。
    目前我自己的实现是:只做了 dstIP 的路由规则,后续会抽空加上大佬的 srcIP 路由匹配策略。

    另外咨询一个问题,大佬的 DNS route 的有效期是怎么计算的?什么时候废止这条路由表?
    是靠客户端的 DNS 请求刷新吗?
    Jirajine
        45
    Jirajine  
       192 天前
    @povsister #41 但因为丢失了信息,有些问题是没有办法在网关正确、可靠的解决的。有些情况对客户端透明非常重要,另一些情况就不一定有这种需求。
    maybeonly
        46
    maybeonly  
    OP
       192 天前
    @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 发出

    系统的路由表实现源*目的匹配各种不方便,更别说超时了。这也是自己搓二进制的主要原因。
    povsister
        47
    povsister  
       192 天前
    @maybeonly #46
    完全理解了!
    也就是只通过 DNS query 去刷新 DNS route 规则,已建立的连接依赖 conntrack 保持,不受 DNS route 限制。

    我昨天给自己的实现添加了 onConnectionActivity 时,重置对应 DNS Route 的有效期,只要现有连接上有数据活动,则对应路由条目会一直刷新有效期。直到所有连接断开,且没有对应 DNS 请求时,对应 DNS route 才会被清理掉。
    用于应对客户端 DNS 记录的缓存时间超过 DNS TTL 的情况。不过感觉似乎有点矫枉过正了。。
    povsister
        48
    povsister  
       192 天前
    @Jirajine #45
    同意的,各种方案都有其优缺点。我描述的多网站共享 anycast ip 问题,确实适合在客户端侧自行解决。网关上只能有限的进行优化,无法彻底根治。属于透明代理的 tradeoff 了。
    Donahue
        49
    Donahue  
       192 天前
    好复杂,我选择旁路由+openclash+机场下发代理文件
    innoxa
        50
    innoxa  
       191 天前
    高端玩家
    dvbs2000
        51
    dvbs2000  
       191 天前
    我用普通人理解翻译一下


    这是一个自己动手实现的软路由系统。设计目标是为了玩得开心,而不是追求极致性能。它采用模块化设计,面向数据包和连接,使用真实 IP 地址。虽然结构复杂,但条理清晰,拓展性好,能够轻松适配各种形式的 VPN 。

    基础操作系统使用 Rocky Linux 9 。主要使用 bash 脚本语言编写,部分模块使用 C++。没有图形界面,但有一些命令行工具。使用了多个内网 IPv4 和 IPv6 网段,尽管并非所有 IP 段都是必须的。

    核心是 NET-IX 模块,提供虚拟交换功能。它使用一段内网 IPv4 和 IPv6 地址,提供调度、故障自动处理、分流及 DNS 、访问控制等服务。其中的主要功能模块包括:

    1. INGW:内网接入网关,可针对不同设备应用不同策略,如 DNS 等。
    2. DNSIW:普通 DNS 服务,转发到运营商 DNS 。
    3. DNSOW:过墙 DNS 服务,对白名单域名转发 DNSIW,其他使用自己的递归 DNS 。
    4. OWGW:隧道入口,对白名单 IP 转发至 RTGW 。
    5. RTGW:IX 网段的出口网关,连接到 WAN 。
    6. DNSROUTE:DNS 分流/策略分流模块,自行开发。

    NET-LAN 模块对应普通内网接入,可直接分配公网 IPv6 地址并使用 SNPT 做转换。

    NET-WAN 模块对应宽带接入,支持 PPPoE 、DHCP 等方式,还能转发 IPTV 流量。RTGW 连接 IX 和 WAN,对多宽带使用 connmark 解决连入连接问题。每条宽带使用两个 netns,分别连 ISP 和 TUN 。

    NET-TUN 是模块化连出 VPN,每条隧道有两个 netns:TN 和 FW 。TN 接受 OWGW 或 DNSROUTE 的流量,经隧道协议发出;FW 提供防火墙和出口路由。VPN 出口在 OWGW 进行调度并自动检查隧道健康状态。

    DNSROUTE 使用 C++ 实现,可匹配 DNS 请求并转发,并对返回的 IP 记录,后续可据此对数据包转发。

    NET-XC 模块将连入 VPN 导入 INGW 并应用三层策略。NET-IoT 模块隔离物联网流量。

    这个设计很灵活,在外部可方便地增删隧道、调整健康检查、改 WAN 设置等,且不影响整体。还能较容易增加新功能,如指定源 IP 经特定隧道、隧道串接、针对特定 WiFi 的 DNS 解锁等。

    总之,这是一个为了玩得开心而设计的软路由系统,结构复杂但条理清晰,模块化程度高,可玩性强。作者在设计时着重考虑了灵活性和可扩展性。


    @maybeonly 宠妻狂魔啊
    小心惯坏

    话说太复杂了 其实软路由稍微需要处理的主要是 dns 部分
    dns 主要防被解析到沟里去
    自己造轮子 说明技术水平确实高
    GotKiCry
        52
    GotKiCry  
       190 天前
    太复杂了哥,我对软路由的理解还是停留在即插即用稳定上个网
    kenywei001
        53
    kenywei001  
       190 天前
    这就有点高端了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2806 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 13:29 · PVG 21:29 · LAX 06:29 · JFK 09:29
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.