MetaCubeX / mihomo

A simple Python Pydantic model for Honkai: Star Rail parsed data from the Mihomo API.
https://wiki.metacubex.one
MIT License
16.05k stars 2.6k forks source link

[Feature] 对IP规则进行匹配时,允许自定义优先解析ipv4/ipv6 #747

Open Nishiki-Asumi opened 1 year ago

Nishiki-Asumi commented 1 year ago

Verify steps

Description

在某些网络环境下(如教育网),ipv4和ipv6的计费、限速规则、稳定性等可能有所不同。目前我使用规则对ipv4和ipv6分开处理(ipv4走代理,ipv6直连),不过经测试发现,除非在应用程序中强制指定使用ipv6(如curl -6,此时一切正常),否则总会匹配ipv4的规则,并使用ipv4进行连接。希望能够增加对应的配置,优先使用指定的协议

curl:

启用TUN:
curl -Iv https://mirrors.tuna.tsinghua.edu.cn
*   Trying 101.6.15.130:443...
* Connected to mirrors.tuna.tsinghua.edu.cn (101.6.15.130) port 443
......
curl -6Iv https://mirrors.tuna.tsinghua.edu.cn
*   Trying [2402:f000:1:400::2]:443...
* Connected to mirrors.tuna.tsinghua.edu.cn (2402:f000:1:400::2) port 443
......
关闭TUN:
curl -Iv https://mirrors.tuna.tsinghua.edu.cn 
*   Trying [2402:f000:1:400::2]:443...
* Connected to mirrors.tuna.tsinghua.edu.cn (2402:f000:1:400::2) port 443
......

在浏览器中的结果类似,对纯ipv6网站可以正确分流,双栈网站则会走ipv4

可能相关的规则配置:

  - AND,((IP-CIDR6,::/0),(GEOIP,CN)),cn6
  - AND,((IP-CIDR,0.0.0.0/0),(GEOIP,CN)),cn4
  - GEOIP,CN,cn

日志:

23-09-15 18:01:10[ info ][TCP] 198.18.0.1:41348 --> mirrors.tuna.tsinghua.edu.cn:443 match GeoIP,CN using cn[PROXY1]
23-09-15 18:01:10[ debug ]handshake success
23-09-15 18:01:09[ debug ]use initial random HelloID:Chrome
23-09-15 18:01:09[ debug ]cn4 match Pass rule
23-09-15 18:01:09[ debug ][Rule] use default rules
23-09-15 18:01:09[ debug ][Sniffer] Sniff TCP [198.18.0.1:41348]-->[mirrors.tuna.tsinghua.edu.cn:443] success, replace domain [mirrors.tuna.tsinghua.edu.cn]-->[mirrors.tuna.tsinghua.edu.cn]
23-09-15 18:01:09[ debug ][DNS] mirrors.tuna.tsinghua.edu.cn --> [2402:f000:1:400::2], from udp://1.1.1.1:53
23-09-15 18:01:09[ debug ][DNS] mirrors.tuna.tsinghua.edu.cn --> [101.6.15.130], from udp://1.1.1.1:53
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from udp://1.1.1.1:53
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from https://dns.alidns.com:443/dns-query
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from https://1.0.0.1:443/dns-query
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from https://1.1.1.1:443/dns-query
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from udp://1.0.0.1:53
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from udp://1.1.1.1:53
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from https://1.0.0.1:443/dns-query
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from https://dns.alidns.com:443/dns-query
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from https://1.1.1.1:443/dns-query
23-09-15 18:01:09[ debug ][DNS] resolve mirrors.tuna.tsinghua.edu.cn from udp://1.0.0.1:53
23-09-15 18:01:09[ debug ][DNS] hijack udp:4.2.2.1:53 from 198.18.0.1:51956

Possible Solution

我认为问题可能在于规则匹配时进行的DNS解析会无条件优先返回ipv4结果,导致对于有双栈支持的域名永远匹配不到ipv6规则(具体讨论在 https://github.com/MetaCubeX/Clash.Meta/issues/747#issuecomment-1752269613 )。

一种可能的方案:

允许在规则中指定优先解析ipv6,例如:

- RULE-SET,cnv6,DIRECT,prefer-resolve-ipv6

并在匹配到这条规则时,检查该域名是否有有效的AAAA记录,如有则使用ipv6结果对这条规则进行匹配

因为我没有学过go,所以无法提供具体的实现,还请谅解

giveup commented 1 year ago

可能需要部署一个上游dns服务器,例如SmartDNShttps://pymumu.github.io/smartdns/config/dualstack/

Nishiki-Asumi commented 1 year ago

可能需要部署一个上游dns服务器,例如SmartDNShttps://pymumu.github.io/smartdns/config/dualstack/

试了一下,smartdns的双栈优选只能屏蔽ipv6地址,不能在有ipv6的情况下屏蔽ipv4结果,这种情况下clash仍然会优先使用ipv4。如果上述结论有误,还请指教

giveup commented 1 year ago

dnsmasq是可以的,可以按需实现只返回ipv4或ipv6。 WX20231009-030855@2x 但是好像不支持按域名来动态只返回ipv4或ipv6。 你这个需求应该通过按域名分发到不同的dns,比如dnsmasq实现只返回ipv6,然后其他dns正常解析。

Skyxim commented 1 year ago

你可以自定义一个 direct 类型的节点

https://github.com/MetaCubeX/Clash.Meta/commit/0a7b7894bd450bf9c262fb1ea891efd1a71f5979 文档好像忘了写具体但是一些基本参数照着其他的应该能看懂

Nishiki-Asumi commented 1 year ago

你可以自定义一个 direct 类型的节点

0a7b789 文档好像忘了写具体但是一些基本参数照着其他的应该能看懂

提issue前我试过这个,相关规则如下:

proxies:
  - {
      name: DIRECT-prefer-v6,
      ip-version: ipv6-prefer,
      type: direct  # for now requires Clash.Meta Alpha
    }

proxy-groups-base: &proxy-groups-base
  proxies:
    - PROXY
    - DirectShouldPreferIPv6
    - PASS
    - REJECT

proxy-groups:
  - name: DirectShouldPreferIPv6
    type: select
    proxies:
      - DIRECT-prefer-v6
      - DIRECT

  - name: cn
    type: select
    <<: *proxy-groups-base

  - name: cnv4
    type: select
    <<: *proxy-groups-base

  - name: cnv6
    type: select
    <<: *proxy-groups-base

rules:
  - AND,((IP-CIDR6,::/0),(OR,(GEOSITE,geolocation-cn),(GEOIP,CN))),cnv6
  - AND,((IP-CIDR,0.0.0.0/0),(OR,(GEOSITE,geolocation-cn),(GEOIP,CN))),cnv4
  - OR,((GEOSITE,geolocation-cn),(GEOIP,CN)),cn

结果行为没有变化。

我看了规则匹配和DNS解析相关部分的源码,如果没有理解错的话,基于IP的规则在匹配时,若当前连接没有解析好的IP,那么就会调用ResolveIP方法,根据注释,这个方法会无条件优先返回ipv4的结果(如有)。相关代码: https://github.com/MetaCubeX/Clash.Meta/blob/ae557c30d3805ca8f58d9c3301e8c9321d5218c4/tunnel/tunnel.go#L586-L656 https://github.com/MetaCubeX/Clash.Meta/blob/ae557c30d3805ca8f58d9c3301e8c9321d5218c4/component/resolver/resolver.go#L194-L211

我不是很确定对调这个方法的返回顺序能否解决问题,我稍后会尝试一下。不过我认为这样做可能带来一些其他的问题,显而易见的是这可能会让ipv4的规则失效,就像现在ipv6的规则一样

Skyxim commented 1 year ago

直接创建一个 prefer-v4 的 direct 替代默认 DIRECT 就行了,不需要加额外规则,如果是双栈域名将优先使用 v4,如果只有 v6 将采用 v6 ps. IP-CIDR 规则这里,的确是一直存在的问题

Nishiki-Asumi commented 1 year ago

直接创建一个 prefer-v4 的 direct 替代默认 DIRECT 就行了,不需要加额外规则,如果是双栈域名将优先使用 v4,如果只有 v6 将采用 v6 ps. IP-CIDR 规则这里,的确是一直存在的问题

但我的需求是对有ipv6和没有ipv6支持的网站进行分流,这个用prefer-v4解决不了

Nishiki-Asumi commented 1 year ago

@xishang0128 已按照新的issue模板修正了格式,并补充了问题描述

Arxhexa commented 1 year ago

确实会舍弃ipv6的解析地址

time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [2402:f000:1:400::2], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [101.6.15.130], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> 101.6.15.130"

之后链接发起也是通过本地ipv4地址

time="2023-10-11T20:54:42.006791+08:00" level=info msg="[TCP] 198.18.0.1:52292 --> mirrors.tuna.tsinghua.edu.cn:443 match Match using v6direct"

但是我自己测试发现,在校园网只认证ipv6,直连配置ip-version: ipv6的情况下

- {name: v6direct, type: direct, ip-version: ipv6, udp: true}

可以以ipv6单栈对目标网站进行访问,我猜测对ipv6的分流可能已经生效但是在log中没有正确呈现

time="2023-10-11T21:19:20.0227056+08:00" level=info msg="[TCP] 198.18.0.1:53236 --> www.nodeseek.com:443 match DomainSuffix(nodeseek.com) using v6direct"
Nishiki-Asumi commented 1 year ago

确实会舍弃ipv6的解析地址

time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [2402:f000:1:400::2], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [101.6.15.130], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> 101.6.15.130"

之后链接发起也是通过本地ipv4地址

time="2023-10-11T20:54:42.006791+08:00" level=info msg="[TCP] 198.18.0.1:52292 --> mirrors.tuna.tsinghua.edu.cn:443 match Match using v6direct"

但是我自己测试发现,在校园网只认证ipv6,直连配置ip-version: ipv6的情况下

- {name: v6direct, type: direct, ip-version: ipv6, udp: true}

可以以ipv6单栈对目标网站进行访问,我猜测对ipv6的分流可能已经生效但是在log中没有正确呈现

time="2023-10-11T21:19:20.0227056+08:00" level=info msg="[TCP] 198.18.0.1:53236 --> www.nodeseek.com:443 match DomainSuffix(nodeseek.com) using v6direct"

其实不是,你不设置直连ip-version一样是可以在纯ipv6环境下正常使用的。如果我没有理解错的话,Clash在分流完成之后会把流量传给对应的出口,至于如何访问则会交由对应的出口处理。在纯ipv6环境下,出口尝试ipv4和ipv6连接,其中ipv4失败,则会回落到ipv6,这个过程不涉及分流(因为分流工作在流量交给出口前已经完成了)。我前面附的curl输出主要是为了证明Clash DNS会优先返回ipv4结果,而出口实际使用ipv4还是ipv6来访问目标网站是未知的

一个简单的验证方式就是,在纯ipv6环境下,打开clash,启用ipv6,全部流量DIRECT

curl -4Iv https://mirrors.tuna.tsinghua.edu.cn
curl -Iv --resolve mirrors.tuna.tsinghua.edu.cn:443:101.6.15.130 https://mirrors.tuna.tsinghua.edu.cn

是可以正常访问的(虽然curl的输出会显示使用ipv4地址连接,但是实际clash是使用ipv6访问目标网站的,可以用wireshark等工具观察到),而

curl -Iv https://mirrors4.tuna.tsinghua.edu.cn

不行(因为该域名没有AAAA记录)

因为我目前不在教育网环境,我是通过禁用ipv4和在路由器端丢弃ipv4数据包来实现纯ipv6的,在教育网下的行为可能略有不同,不过我认为结果应当是类似的

Arxhexa commented 1 year ago

确实会舍弃ipv6的解析地址

time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [2402:f000:1:400::2], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [101.6.15.130], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> 101.6.15.130"

之后链接发起也是通过本地ipv4地址

time="2023-10-11T20:54:42.006791+08:00" level=info msg="[TCP] 198.18.0.1:52292 --> mirrors.tuna.tsinghua.edu.cn:443 match Match using v6direct"

但是我自己测试发现,在校园网只认证ipv6,直连配置ip-version: ipv6的情况下

- {name: v6direct, type: direct, ip-version: ipv6, udp: true}

可以以ipv6单栈对目标网站进行访问,我猜测对ipv6的分流可能已经生效但是在log中没有正确呈现

time="2023-10-11T21:19:20.0227056+08:00" level=info msg="[TCP] 198.18.0.1:53236 --> www.nodeseek.com:443 match DomainSuffix(nodeseek.com) using v6direct"

其实不是,你不设置直连ip-version一样是可以在纯ipv6环境下正常使用的。如果我没有理解错的话,Clash在分流完成之后会把流量传给对应的出口,至于如何访问则会交由对应的出口处理。在纯ipv6环境下,出口尝试ipv4和ipv6连接,其中ipv4失败,则会回落到ipv6,这个过程不涉及分流(因为分流工作在流量交给出口前已经完成了)。我前面附的curl输出主要是为了证明Clash DNS会优先返回ipv4结果,而出口实际使用ipv4还是ipv6来访问目标网站是未知的

一个简单的验证方式就是,在纯ipv6环境下,打开clash,启用ipv6,全部流量DIRECT

curl -4Iv https://mirrors.tuna.tsinghua.edu.cn
curl -Iv --resolve mirrors.tuna.tsinghua.edu.cn:443:101.6.15.130 https://mirrors.tuna.tsinghua.edu.cn

是可以正常访问的(虽然curl的输出会显示使用ipv4地址连接,但是实际clash是使用ipv6访问目标网站的,可以用wireshark等工具观察到),而

curl -Iv https://mirrors4.tuna.tsinghua.edu.cn

不行(因为该域名没有AAAA记录)

因为我目前不在教育网环境,我是通过禁用ipv4和在路由器端丢弃ipv4数据包来实现纯ipv6的,在教育网下的行为可能略有不同,不过我认为结果应当是类似的

抱歉我没有说明我启用了内置dns和tun,按我理解tun工作在第三层已经完成了ip封包,物理网卡此时应该仅传送包而不再参与ipv4或是ipv6的选择。所以我测试了单栈v6情况下直连能否访问目标网站。 不过后来想了一下发现我之前说的没什么太大意义,按域名是能单独设置,但由于dns默认选择v4地址所以通过geoip对v6的分流还是无法实现

Arxhexa commented 1 year ago

再补充说明一下,我没有用过go,所有的推论只是基于控制变量和查看log

指定ip-version为ipv6是为了确保clash仅通过ipv6建立连接,这一点我在同时有v4/v6地址的情况下试验过,是没有问题的 校园网仅单栈v6认证是为了确保物理网卡的ipv4地址无法访问外网,这样任何的外网连接都不可能通过ipv4地址建立

此时启用clash的tun

访问双栈支持的网站在log中会显示通过tun的ipv4地址与目标网站ipv4地址建立连接,由于网站可以正常加载同时无论是clash还是物理网卡都不可能让流量经过ipv4,所以我认为底层连接实际是通过ipv6建立的,虽然在log中不是这样 访问单栈ipv6的网站如果我没记错似乎在log中不会出现任何记录

虽然geoip规则由于默认返回ipv4地址无法分流,但geosite规则可能是可以做到的,比如说 -GEOSITE, cnipv6, v6direct 但我不确定是否会有人维护这样一个域名列表

LordHumphrey commented 1 year ago

直接创建一个 prefer-v4 的 direct 替代默认 DIRECT 就行了,不需要加额外规则,如果是双栈域名将优先使用 v4,如果只有 v6 将采用 v6 ps. IP-CIDR 规则这里,的确是一直存在的问题

proxies:
  - {
      name: DIRECT-prefer-v6,
      ip-version: ipv6-prefer,
      type: direct  # for now requires Clash.Meta Alpha
    }
  1. 此处的proxy,是否支持指定网卡,本人机器接了两个运营商网络,希望能做到国内走御三家且v4优先,国外的走教育网。
  2. 感谢开发者的辛苦付出,Meta内核非常优秀~
Arxhexa commented 7 months ago

ipcidr6的问题应该是修复了,今天试了一下- IP-CIDR6,::/0,v6direct对存在ipv6地址的双栈网站可以匹配直连 1710927776779

你可以尝试使用校园网ipv4访问b站,分配bilivideo的cdn可以匹配 IP-CIDR6,::/0 的规则

letsky192 commented 5 months ago

确实会舍弃ipv6的解析地址

time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [2402:f000:1:400::2], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [101.6.15.130], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> 101.6.15.130"

之后链接发起也是通过本地ipv4地址

time="2023-10-11T20:54:42.006791+08:00" level=info msg="[TCP] 198.18.0.1:52292 --> mirrors.tuna.tsinghua.edu.cn:443 match Match using v6direct"

但是我自己测试发现,在校园网只认证ipv6,直连配置ip-version: ipv6的情况下

- {name: v6direct, type: direct, ip-version: ipv6, udp: true}

可以以ipv6单栈对目标网站进行访问,我猜测对ipv6的分流可能已经生效但是在log中没有正确呈现

time="2023-10-11T21:19:20.0227056+08:00" level=info msg="[TCP] 198.18.0.1:53236 --> www.nodeseek.com:443 match DomainSuffix(nodeseek.com) using v6direct"

alpha-df01582还是这样,解析双栈域名后,v6 没参与分流。不过分流之后仍然会同时向 v4、v6 发起连接(日志没写,但看到有 v6 的连接)。

Arxhexa commented 5 months ago

确实会舍弃ipv6的解析地址

time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [2402:f000:1:400::2], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> [101.6.15.130], from udp://172.21.0.21:53"
time="2023-10-11T20:54:41.9983704+08:00" level=debug msg="[DNS] mirrors.tuna.tsinghua.edu.cn --> 101.6.15.130"

之后链接发起也是通过本地ipv4地址

time="2023-10-11T20:54:42.006791+08:00" level=info msg="[TCP] 198.18.0.1:52292 --> mirrors.tuna.tsinghua.edu.cn:443 match Match using v6direct"

但是我自己测试发现,在校园网只认证ipv6,直连配置ip-version: ipv6的情况下

- {name: v6direct, type: direct, ip-version: ipv6, udp: true}

可以以ipv6单栈对目标网站进行访问,我猜测对ipv6的分流可能已经生效但是在log中没有正确呈现

time="2023-10-11T21:19:20.0227056+08:00" level=info msg="[TCP] 198.18.0.1:53236 --> www.nodeseek.com:443 match DomainSuffix(nodeseek.com) using v6direct"

alpha-df01582还是这样,解析双栈域名后,v6 没参与分流。不过分流之后仍然会同时向 v4、v6 发起连接(日志没写,但看到有 v6 的连接)。

IP-CIDR6,::/0 是可以分流的,我上面那张图里就是,不过我没有试过更小的地址块能否参与分流

yueziji commented 5 days ago

同样需求,但貌似现在对于双栈网站仍然是优先返回v4进行匹配