Skip to content

Conversation

@LjhAUMEM
Copy link
Contributor

@LjhAUMEM LjhAUMEM commented Jan 18, 2026

将 kcp header/security 移到 udpmasks

整理的过程发现两个问题,1. header 双端发送格式都是一样,2. kcp 的 ReadBuffer 参数并没有用到,不过都是以后再说

改动太大需要测试

已测试的配置,新旧 客 服 可双双互连

新客户端配置
{
  "log": { "loglevel": "debug" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "address": "127.0.0.1",
        "port": 1081,
        "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
        "encryption": "none"
      },
      "streamSettings": {
        "network": "kcp",
        "kcpSettings": {
        },
        "udpmasks": [
          {
            "type": "dtls"
          },
          {
            "type": "aesgcm128",
            "settings": {
              "psk": "123"
            }
          }
        ]
      }
    }
  ]
}
新服务端配置
{
  "log": { "loglevel": "debug" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1081,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "5783a3e7-e373-51cd-8642-c83782b807c5"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "kcp",
        "kcpSettings": {
        },
        "udpmasks": [
          {
            "type": "dtls"
          },
          {
            "type": "aesgcm128",
            "settings": {
              "psk": "123"
            }
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}
旧客户端配置
{
  "log": { "loglevel": "debug" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "address": "127.0.0.1",
        "port": 1081,
        "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
        "encryption": "none"
      },
      "streamSettings": {
        "network": "kcp",
        "kcpSettings": {
          "seed": "123",
          "header": {
            "type": "dtls"
          }
        }
      }
    }
  ]
}
旧服务端配置
{
  "log": { "loglevel": "debug" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1081,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "5783a3e7-e373-51cd-8642-c83782b807c5"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "kcp",
        "kcpSettings": {
          "seed": "123",
          "header": {
            "type": "dtls"
          }
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}

@LjhAUMEM
Copy link
Contributor Author

先放几天吧我自己也要 review 一下

@Fangliding
Copy link
Member

让kcp能用 salamander 就差不多得了吧 那堆伪装就是兼容选项 这一break反而让它们没有意义了 让AI把它挪到udp mask就是把旧史搬上去
而且它只伪装头 从这个角度还不如那堆udp noise

@LjhAUMEM
Copy link
Contributor Author

让AI把它挪到

并不是 AI,我移了几天

它只伪装头

这个头确实有问题,服务端回包格式和客户端发包格式是一样的,也许以后根据 rfc 改下就有资格上桌了

从这个角度还不如那堆udp noise

noise 要延迟发包和只加头或者异或/加密混淆不同,还不打算看这个,如果不需要 kcp mask 我就先去看 hy transport hub 了

@Fangliding
Copy link
Member

那还是先弄hy入站吧 这个有用点 不然复现问题还得让人装个singbox非常难绷

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

正想把分享链接中那个 mKCP seed 删掉给 VLESS Seed 让位,所以 udpmasks 里别再叫 seed 了改名 psk 之类的吧

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

如果不是真正的前面就加个 fake- 与以后要加的真正的区别开吧也不用放 header 文件夹里了,另外 rebase 一下,test 修一下

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

@Fangliding 现在有了 hy2 要不把 kcp 删了吧,没几个人用就是说,现在想暴力发包的都去用 hy2 了

如果想用 VLESS 的 reverse 等特性,VLESS 也能接上 hy2 传输层,等 Xray 有了 hy2 inbound 的时候

@Fangliding
Copy link
Member

别 有人用的

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

他们用的逻辑不就是 kcp 还有那些伪装吗,那些伪装会被迁移到 udpmasks 然后催 UI 更新一下就能完全取代 kcp 了

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

我是觉得 UDP 可靠传输/暴力发包这块比起来土制协议还是全部基于 QUIC 更好,要什么外观把伪装叠上去就行

@Fangliding
Copy link
Member

我觉得一个更精简的UDP协议还是有必要的 quic太重了 自己想改点都无从下手

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

也是那就先留着吧,不过等 udpmasks 弄差不多了、Xray 支持 hy2 服务端了,估计用 kcp 更没人用了,机场甚至推出 Xray 版 hy2

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

@LjhAUMEM 话说这 QUIC 库方便改 UDP 包最大长度不,不然 udpmasks 叠两层就炸了

@RPRX
Copy link
Member

RPRX commented Jan 18, 2026

@LjhAUMEM 还有 udpmasks 层能实现个统一的预留长度不,不然对于先弄成全随机数再叠上 fake header 来说性能上太吃亏了

@LjhAUMEM
Copy link
Contributor Author

@LjhAUMEM 话说这 QUIC 库方便改 UDP 包最大长度不,不然 udpmasks 叠两层就炸了

好像不受 quic 包长度限制,因为到服务端也是先经过 pktconn 解包了才会到 quic,长度恢复正常,只是大包在公网应该更容易丢包

@LjhAUMEM 还有 udpmasks 层能实现个统一的预留长度不,不然对于先弄成全随机数再叠上 fake header 来说性能上太吃亏了

每个连接搞个全局缓冲区吗,感觉没必要啊,损失点就损失点吧

@RPRX
Copy link
Member

RPRX commented Jan 19, 2026

就是因为大包在公网更容易丢包所以才得限制原始 UDP 包最大长度以便叠上多层 udpmasks 后不容易丢包

无优化的“先弄成全随机数再叠上 fake header”直接多了一次没必要的 copy,所以肯定先要给后面的 udpmask 留一些头部空间

@LjhAUMEM
Copy link
Contributor Author

就是因为大包在公网更容易丢包所以才得缩短原始 UDP 包长度以便叠上多层 udpmasks 后不会丢包

看了下只能设置 datagram 的包长度大小,对于 steam 的只能设置缓冲区大小,再深入就得去魔改 quic-go 了

无优化的“先弄成全随机数再叠上 fake header”直接多了一次没必要的 copy,所以肯定先要给后面的 udpmask 留一些头部空间

应该可以在第一次 writeto 获取后面 header 的 size 和加密的 overhead 一起申请了,我看看,但是如果后面是个 salamander 可能还是多一次 copy,salamander 自己搞了个缓冲区

@RPRX
Copy link
Member

RPRX commented Jan 19, 2026

XOR 的本来就要多 copy 一次倒无所谓,只是有没有复用原内存的问题,不过 AEAD 的话

@RPRX
Copy link
Member

RPRX commented Jan 19, 2026

不对,AEAD 的复用不了内存只是针对一整条信息分多个块,而对于分包发送,有尾部空间的话还是可以复用内存的

@LjhAUMEM
Copy link
Contributor Author

LjhAUMEM commented Jan 19, 2026

重构了一下 salamander 改为 buf 的实现,删掉了独有的缓冲区

现在每个 udpmask 都支持作为第一个 writeto 时为后面的写入预留空间,我自己都绕进去了,还需要再看下

aead 加解密后的长度是会变化的,好消息是不会影响预留出来的空间,但是会延长后面的空间,如果作为 first 时申请的空间不够多可能会出问题

@LjhAUMEM
Copy link
Contributor Author

其实 salamander 搞这个缓冲区还是不错的,不然并发写入都 stacknew 一下有点难崩,可以只在 first 维护一个 8192 的缓冲区,后面的都共享这块读写

@LjhAUMEM
Copy link
Contributor Author

看了下 aead 加密后的长度其实是固定的,为 overhead+len(plaintext),然后 nonce 也要传过去再加个 nonceSize,我写了个 test 可以跑一下,顺便把不太规范的 simple open 改了

udpmask 应该可以了,剩下的就是上配置文件测试,如果测试没问题再看下 kcp 和删掉的部分应该没了

@LjhAUMEM
Copy link
Contributor Author

顶格 255

255 只是 query name 字段的限制,整个包应该不止,而且查询的域名总是满的也不对吧,虽然作为隧道也不会短

@RPRX
Copy link
Member

RPRX commented Jan 24, 2026

那你再研究一下吧,而且似乎它们传数据不是 query name 而是 txt 记录?

@Fangliding
Copy link
Member

整个DNS payload 加在一起不能超过512 之前研究过的 考虑到DNS Message还有其他东西要长也长不了很多
#4516 (comment)

@LjhAUMEM
Copy link
Contributor Author

那你再研究一下吧,而且似乎它们传数据不是 query name 而是 txt 记录?

对,回传是 txt,1035 的 4.1.3 和 3.3.14

大小应该不太是问题,就是根据查询才能回包的机制有点恶心,好像还要搞个队列存服务器 writeto 的消息,还没看懂 turbotunnel 的 clientid 是不是必须的

@RPRX
Copy link
Member

RPRX commented Jan 24, 2026

好像还要搞个队列存服务器 writeto 的消息

这个直接用 pipe 就行

@RPRX
Copy link
Member

RPRX commented Jan 24, 2026

就是根据查询才能回包的机制有点恶心

或许返回的时候告知客户端服务端还缓存了多少数据以触发多个并行查询,不过用户要配置个请求速率上限

空闲的时候也是,用户需要配置个一秒一次查询,合起来就是请求速率上下限吧,支持范围与随机

clientid

这个是用来区分承载的不同 TCP 流的吗

@RPRX
Copy link
Member

RPRX commented Jan 24, 2026

@LjhAUMEM 话说 udphop 的 port 是 range,interval 不是 range 的话我还是感觉很难受,有空时加一下吧,每次随机

之前没让加是因为 udphop 本来就是强特征就无所谓,且官方版本没支持 range、分享链接也不支持,不过每次想起来都难受

@LjhAUMEM
Copy link
Contributor Author

LjhAUMEM commented Jan 24, 2026

这个是用来区分承载的不同 TCP 流的吗

从行为上来看不是,把每个客户端的 pktconn 生成一个 clientid,服务端那边再存一个 clientid 和 addr 的 map,可能会有多个 clientid 对应一个 addr(不对,应该就是一个 clientid 对应一个 addr),收到一个合法查询包再拿这个包的 clientid 从 writeto 队列里 pop 出一个包回传

而且也不需要,上层 tcp 归 kcp session 管

@LjhAUMEM 话说 udphop 的 port 是 range,interval 不是 range 的话我还是感觉很难受,有空时加一下吧,每次随机

ok,没问题,把 dnstt 完成之后就加

@RPRX
Copy link
Member

RPRX commented Jan 24, 2026

udphop 那个 range 先加吧,没几行代码,另起一个 PR

不用管 DNSTT 具体是怎么实现的,参考基本思路就行,不同的被代理的流肯定要加个 ID 来区分的放到 query name 中

@gfw-killer
Copy link

Could you please add a Custom Mask for both Inbound and Outbound to mimic different UDP and TCP services?
The ones that you add by default will be targeted by the GFW, it's always better to have custom ones

And my suggestion about DNSTT is that if you added it, please don't mention or document it so it get less attention, as it's easy to block
Xray clients already support JSON configs, people who can read your code will be able to use it on all of their devices
If it get a share link or supported by GUI clients or appear in the official documents, it will get blocked in all countries faster than what we think

@LjhAUMEM
Copy link
Contributor Author

Could you please add a Custom Mask for both Inbound and Outbound to mimic different UDP and TCP services?
The ones that you add by default will be targeted by the GFW, it's always better to have custom ones

freedom 出站的 udp noise 有计划加入,但应该不在这个 pr

And my suggestion about DNSTT is that if you added it, please don't mention or document it so it get less attention, as it's easy to block
Xray clients already support JSON configs, people who can read your code will be able to use it on all of their devices
If it get a share link or supported by GUI clients or appear in the official documents, it will get blocked in all countries faster than what we think

所以放在 mask 里,只是个可选的玩具,但这放在只能使用 DNSTT 的国家确实是个问题,不过暂时不会加入 DOT 和 DOH,如果封禁了还可以用这两个

@RPRX
Copy link
Member

RPRX commented Jan 25, 2026

If it get a share link or supported by GUI clients or appear in the official documents, it will get blocked in all countries faster than what we think

我觉得对于 DNSTT 这种已经人尽皆知的翻墙方式来说已经区别不大了

@gfw-killer
Copy link

You are right

About the Custom Mask, outbound already has noises feature, but the Inbound should be able to Approve it, and have a behavior to answer unauthenticated requests, or forwarded the unauthenticated incoming request to a real local server or remote server then forward it's response to the unauthenticated request

And there is nothing for TCP also

@RPRX
Copy link
Member

RPRX commented Jan 25, 2026

@LjhAUMEM 人家 DNSTT 是似乎是专有名称,不是实现那个协议的话换个名吧,比如 XDNS

@LjhAUMEM
Copy link
Contributor Author

@LjhAUMEM 人家 DNSTT 是似乎是专有名称,不是实现那个协议的话换个名吧,比如 XDNS

哦哦可以,收尾了再改吧写,的太快了还不知道能不能跑起来

@LjhAUMEM
Copy link
Contributor Author

LjhAUMEM commented Jan 26, 2026

已在本地测试跑通,明天测试下公共 DNS 转发

顺便说下 mtu 计算方法,客户端最大 253(query name),域名中的点为分割一个 lable,单个 lable 最大 63,其中 8(clientid)+3(padding) 是占死的,还有设置的域名,以及 base32 编码的损耗,服务端最大 934

测试配置

{
  "log": { "loglevel": "debug" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "address": "127.0.0.1",
        "port": 1081,
        "id": "5783a3e7-e373-51cd-8642-c83782b807c5",
        "encryption": "none"
      },
      "streamSettings": {
        "network": "kcp",
        "kcpSettings": {
          "mtu": 100
        },
        "udpmasks": [
          {
            "type": "xdns",
            "settings": {
              "domain": "a.com"
            }
          }
        ]
      }
    }
  ]
}
{
  "log": { "loglevel": "debug" },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 1081,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "5783a3e7-e373-51cd-8642-c83782b807c5"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "kcp",
        "kcpSettings": {
          "mtu": 900
        },
        "udpmasks": [
          {
            "type": "xdns",
            "settings": {
              "domain": "a.com"
            }
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom"
    }
  ]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants