Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Read the relevant document completely before changing that area:
6. Reuse the existing raw-IP netstack and outbound graph. Add no second Windows proxy core, Wintun path, per-protocol socket factory, or fake fd layer.
7. Keep optional protocol/platform code feature- and target-gated. Default non-TUN builds must continue to compile.
8. When copying or modifying third-party source, record the upstream project and preserve all applicable license terms. Audit linked dependencies against the resolved release graph. Do not describe independent rewrites, protocol interoperability, or architectural references as derived source without evidence; Credits provide context and attribution, not a substitute for release license review.
9. Keep code, documentation, package identifiers, examples, and signing inputs host-neutral; application-specific names and credentials belong in downstream integrations.
9. Keep every public surface—code, documentation, tests, examples, commits, issues, pull requests, reviews, CI output, and releases—limited to VCore and public dependencies. Keep private downstream repository or product identities, links, implementation details, status, artifacts, and roadmaps outside this repository and its GitHub surfaces. Before publishing, search the staged diff and proposed GitHub text for downstream identifiers.

# Validation

Expand Down
4 changes: 4 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ VCore turns host-captured IP traffic into routed proxy or direct sessions while
The packaged AppContainer participant that owns one active Windows tunnel session and exchanges its raw-IP packets with the Windows session runtime.
_Avoid_: Plugin, background task when referring to the whole participant, proxy core

**Windows VPN policy**:
The profile-level Always On and destination-bypass choices for a Windows tunnel whose application scope is always global.
_Avoid_: Per-app policy, app selection, traffic filter

**Windows session runtime**:
The VCore runtime serving one active Windows tunnel session inside the Windows session host.
_Avoid_: Foreground runtime, provider runtime, external core
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ char *VCoreInvoke(const char *request_json);
void VCoreFree(char *response);
```

Windows packages also use the revision-2 host bridge for profiles, Session Snapshots, and the optional session backend:
Windows packages also use the revision-3 host bridge for the all-app VPN policy, profiles, Session Snapshots, and the optional session backend:

```c
char *VCoreWindowsVpnInvoke(const char *request_json);
Expand Down Expand Up @@ -99,6 +99,8 @@ DNS opaque cache 64 entries / 256 KiB
GeoData allocation capacity 8 MiB
```

Windows advertises a 1,400-byte L3 MTU as required by `StartWithMainTransport` and caps TUN UDP responses at 1,352 bytes; 1,500 bytes remain the cross-platform parser ceiling.

TCP sessions, ordinary UDP associations, half-open connections, outbound handshakes, and active DNS transports are created on demand. Bounded queues, per-flow buffers, wire/parser limits, timeouts, idle cleanup, and caches provide structural safety. The iOS 35/45 MiB targets are best-effort observations and do not change lifecycle results.

## Documentation
Expand Down
30 changes: 28 additions & 2 deletions docs/acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
- 规则、GeoData、HTTP/TLS/QUIC 嗅探;
- ICMPv4/ICMPv6 Echo、校验和、分片、MTU 和队列满;
- Apple/Android TUN 帧格式、文件描述符副本和关闭所有权;
- Windows 单 Application manifest、Provider/Session Host token 绑定、控制/数据协议、Session Snapshot v2、会合记录、包队列、批量写入、物理网络绑定和 Job Object 多进程监督;
- Windows 单 Application manifest、Provider/Session Host token 绑定、控制/数据协议、Session Snapshot v2、会合记录、包队列、批量写入、物理网络绑定、link-local 与普通 on-link prefix 的独立收集/去重、显式排除优先的本地路由规划、按有效 MTU 计算的 UDP/DNS 响应上限和 Job Object 多进程监督;
- Controller 鉴权、速率和累计流量语义,以及代理组查询、实时选择和有界请求处理。

常用命令:
Expand Down Expand Up @@ -119,11 +119,37 @@ vcore-uwp-demo.exe stop
- Provider/Session Host 退出、管道错误和网络变化时的失败关闭;
- 快速重连、持续压力、零队列丢弃和显式 Stop 清理。

### 全局 VPN policy 与 IPv4-only clean gate(2026-09-01)

当前 Windows 11 ARM64 build 26200.9278 主机重启后,先确认 package-owned route、interface 和进程均为零,再用同一源树构建、开发签名并安装单 Application MSIX。实际结果:
Comment thread
yiguodev marked this conversation as resolved.

- `ipv6: false` 和 `ipv6: true` 都完成稳定 `start → status → TCP traffic → stop → status`;前者运行时有一个 VPN interface,后者有两个,Provider 和 Session Host 都各激活一个;
- 两种地址模式都观察到 Provider ingress/egress,显式 Stop 后 Session Host、route 和 interface 均归零;
- `ipv6: false` 向 `StartWithMainTransport` 传 null IPv6 client-address 参数后通过;传空集合的对照包稳定返回 `0x8007000E`,且不启动 Session Host;
- 主 transport 绑定当前物理地址后,目标 exclusion route 选择物理出口,而未排除的控制流量仍进入 VPN;回环绑定的对照包会使 exclusion traffic 超时;
- `allowLocalNetwork: true` 时实机局域网 peer 选择物理出口;设为 `false` 后,Provider 为当前适配器全部去重的 on-link prefixes 安装更具体的 inclusion routes,路由总数由 6 增至 10,同一 peer 选择 `/25` VPN route,并完成外网双向流量;
- profile 的 Always On capability 在连接时读回为启用,显式 Stop 后保持断开;随后用默认 policy 重建 profile 时读回为禁用;
- 已有 profile/Snapshot 在前台启动命令退出、Provider/Session Host 均不存在时,由系统 profile connect 冷启动两个 native 进程并完成双向流量;
- 类型错误的 policy 在 Provider 激活前被拒绝;运行中强制终止 Session Host 后 Provider 失败关闭,profile 变为 disconnected,route/interface 清零;
- 每个 gate 结束时都确认无 Session Host、route 或 VPN interface 残留。Provider 的已停止 AppContainer 外壳可能暂留,验收脚本在各 case 之间显式结束它。

路由规划修复后,同机开发签名包再次通过 IPv4-only/dual-stack lifecycle 与流量、LAN bypass、IPv4 exclusion/control、Always On、cold profile、失败关闭和零残留门禁。自动化同时覆盖多地址、多 IPv4/IPv6 on-link prefix、重复子网和显式 exclusion 与生成子前缀重叠;实机只探测到一个局域网 peer,未逐一验证每个 on-link prefix。

PR 审查修复后的当前源树再次构建 ARM64 release artifacts,打入同一单 Application 开发签名 MSIX,并通过 `Add-AppxPackage` 安装。使用 bridge `start/status/stop`、`curl`、有接收超时的 UDP probe、`ping.exe`、`Clear-DnsClientCache` 和 `Resolve-DnsName` 实际执行:

- `ipv6: false` 与 `ipv6: true` 都通过 TCP、UDP、ICMPv4 和清缓存 hostname 请求;dual-stack 还通过 ICMPv6;
- 两种地址模式在 `dns.enable: true` 和 `false` 下都通过清缓存 hostname 请求;
- 物理 IPv6 link-local 只进入 on-link prefix 库,不成为 Session Host socket 源地址;`allowLocalNetwork: false` 的 dual-stack case 实际观察到两条对应的更具体 inclusion routes,并保持 TCP、UDP、ICMPv4/ICMPv6 和 hostname 流量;
- LAN allow/block 与 IPv4 exclusion/control 再次通过;每个 case 均观察到 Provider 和 Session Host 激活、稳定 connected、显式 Stop、disconnected,以及 Provider 外壳清理后的零进程、零 route、零 VPN interface;
- 1400 MTU 对应的 1352 字节 TUN/XUDP 与 DNS UDP 响应上限由当前自动化覆盖;本次实包 probe 验证正常大小 UDP 数据面,没有把超限数据报写成成功传输。

该记录证明当前机器上的 IPv4 物理出口、物理 IPv6 link-local route inventory 和 Windows 分配的双栈 VPN interface;主机没有物理 IPv6/default gateway,因此真实 IPv6 exclusion 仍未执行。

Windows 路由必须保留两条 `/1`。在安装包环境中,单条 VPN `/0` 会使按产品要求绑定物理源地址和接口的外层 socket 返回 `WSAENETUNREACH`;两条 `/1` 不会产生该问题。

以下项目由发布开发者在对应机器或服务中验证,不阻塞上述本机可行性结论:

- 单 Application test-signed MSIX 安装
- production-signed MSIX、WACK 与 Store 安装路径
- Windows 10 20H2;
- 原生 x64 Windows;
- 真实物理 IPv6;
Expand Down
15 changes: 11 additions & 4 deletions docs/invoke-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,10 @@ ProtectFd(fd) -> bool

## Windows 安装包桥接

`VCoreWindowsVpnInvoke` 使用独立的桥接修订版 2
`VCoreWindowsVpnInvoke` 使用独立的桥接修订版 3

```json
{"bridgeVersion":2,"method":"getVpnStatus","payload":{}}
{"bridgeVersion":3,"method":"getVpnStatus","payload":{}}
```

只接受六个方法:
Expand All @@ -344,6 +344,11 @@ ProtectFd(fd) -> bool
"dnsIpv4Address": "8.8.8.8",
"dnsIpv6Address": "2001:4860:4860::8888"
},
"policy": {
"alwaysOn": false,
"allowLocalNetwork": true,
"excludedCidrs": []
},
"sessionBackend": {
"processes": [
{
Expand All @@ -355,6 +360,8 @@ ProtectFd(fd) -> bool
}
```

`policy` 始终必填。Windows VPN 固定覆盖所有应用;`alwaysOn` 控制 profile capability,实际自动连接仍取决于 Windows 用户设置和 active profile;`allowLocalNetwork` 控制本地子网是否绕过;`excludedCidrs` 是最多 64 个规范 IPv4/IPv6 目标网段。拒绝重复项、host bits、`/0`、禁用 IPv6 时的 IPv6 项,以及包含当前 VPN DNS 地址的项。

`sessionBackend` 可以省略。存在时包含 `1..=8` 个有序关键进程;每项只有 package installed location 内的规范 `.exe` 相对路径和有界 argv 数组。同一可执行文件可出现多次。第一版不接受 port、UDP、readiness、restart、environment、working directory 或 raw command line;任一进程退出都会使当前 VPN 会话失败关闭。

桥接把 YAML、进程顺序、路径和参数发布为 `vcore-session-v2:<sha256>` Session Snapshot。参数引用的文件由调用方保持存在且不可变,VCore 不读取或摘要其内容。`getVpnStatus.data.snapshotToken` 返回该完整 Session token。
Expand All @@ -365,7 +372,7 @@ ProtectFd(fd) -> bool

- 所有 JSON DTO 拒绝未知字段。
- 配置、错误和日志按 UTF-8 字节计数并受固定上限约束。
- TUN 原始数据包最大 1,500 字节;最终代理 UDP 负载最大 1,452 字节。
- 嵌套 UDP 协议可以增加有界帧头,但解封装后的最终负载仍受 1,452 字节限制
- TUN 原始数据包解析最大 1,500 字节;最终代理 UDP 响应按有效 MTU 减去 48 字节限制,其他 TUN 平台最大 1,452 字节,Windows 最大 1,352 字节。
- 嵌套 UDP 协议可以增加有界帧头,但解封装后的最终负载仍受该平台响应上限限制
- 节点和代理组定义名共享大小写敏感的严格 UTF-8 命名空间;`DIRECT`、`REJECT` 和 `RULES` 不能用作定义名。
- Secret、password、UUID、REALITY key、short ID、目标地址和完整配置不得进入日志。
6 changes: 4 additions & 2 deletions docs/runtime-resource-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

```text
原始包 / MTU 1,500 字节
最终代理 UDP 负载 1,452 字节
最终代理 UDP 负载 1,452 字节(Windows 1,352)
包队列 256
普通事件 / UDP 响应 128
DNS 入站 / DNS 响应 128 / 128
Expand All @@ -27,6 +27,8 @@ TUN 域名提示 256 项(按需)
GeoData 分配容量 8 MiB
```

Windows L3 接口及其 Session Host netstack 使用 1400 MTU,因此按 IPv6 UDP 头保守计算的响应负载上限是 1352;表中的 1500 是跨平台原始包解析上限和其他 TUN 平台的固定 MTU。

- DNS 响应和普通 UDP 响应使用不同队列,但共享 netstack UDP 入站接收器。
- TUN 域名提示只在 TUN 配置实际包含域名规则时创建,并从空容量按需增长。
- ICMP 响应复用原始包出站队列,不创建独立任务或长期状态。
Expand All @@ -52,7 +54,7 @@ GeoData 分配容量 8 MiB
- 只有成功入队的请求或响应刷新活动时间;
- 父运行时停止时删除、取消并等待全部子任务。

嵌套代理协议可以增加有界帧头,但最终解封装负载仍不得超过调用方给出的上限;TUN 最终负载上限为 1,452 字节。
嵌套代理协议可以增加有界帧头,但最终解封装负载仍不得超过调用方按有效 MTU 给出的上限;其他 TUN 平台为 1,452 字节,Windows 为 1,352 字节。

## DNS

Expand Down
2 changes: 1 addition & 1 deletion docs/tun-icmp-dns.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Controller 切换代理组后,只在后续需要创建新 DNS transport 时使
| DNS 响应 | 128 |
| 普通 UDP 响应 | 128 |

DNS 和普通 UDP 响应使用不同队列,但共享 netstack UDP 入站接收器。队列满时只丢当前请求或响应,不阻塞全局 UDP 循环。TUN UDP 响应受 1,452 字节负载上限约束
DNS 和普通 UDP 响应使用不同队列,但共享 netstack UDP 入站接收器。队列满时只丢当前请求或响应,不阻塞全局 UDP 循环。TUN UDP 响应按有效 MTU 减去 48 字节保守限制;其他平台为 1,452 字节,Windows 为 1,352 字节

运行时停止会取消 open、send、receive、retry 和 response-send,释放全部传输并等待已跟踪任务结束。停止返回后不得再向 TUN 回包。

Expand Down
11 changes: 6 additions & 5 deletions docs/tun-platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ VpnChannel callback
Windows 使用 `Windows.Networking.Vpn` 回调,不使用文件描述符或适配器 ring:

- Provider 在回调内复制 `VpnPacketBuffer` 字节,不保存系统缓冲区的借用;
- 顶层 `ipv6: false` 时,Provider 不向 Windows 分配 IPv6 TUN 地址,也不安装 IPv6 路由或 DNS;`startVpn` 的 IPv6 地址字段仍严格必填并经过验证;
- 顶层 `ipv6: false` 时,Provider 向 `StartWithMainTransport` 传 null IPv6 client-address 参数,不分配 IPv6 TUN 地址,也不安装 IPv6 路由或 DNS;`startVpn` 的 IPv6 地址字段仍严格必填并经过验证;
- Windows profile 固定覆盖所有应用;Provider 按 policy 设置本地子网旁路,并把最多 64 条规范目标 CIDR 加入 exclusion routes;
- 系统和 Provider 创建的缓冲区都按 WinRT 所有权规则归还;
- 回调不等待管道 I/O,入站和出站队列保持有界;
- 空到非空的回环唤醒只通知 `Decapsulate` 排空响应队列;
Expand All @@ -55,11 +56,11 @@ Windows 使用 `Windows.Networking.Vpn` 回调,不使用文件描述符或适

## MTU 与结构上限

当前只接受 MTU 1500:
用户 TUN 配置当前只接受 MTU 1500。Windows 因 `StartWithMainTransport` 平台上限对 L3 接口和 Session Host netstack 使用 1400;packet channel 仍保留 1500 字节解析上限

```text
原始 TUN 包 1,500 字节
最终代理 UDP 负载 1,452 字节
最终代理 UDP 负载 1,452 字节(Windows 1,352)
包队列 256
普通事件 / UDP 响应 128
DNS 入站 / DNS 响应 128 / 128
Expand All @@ -70,8 +71,8 @@ TCP 会话、普通 UDP 关联、半开连接和出站握手不设固定业务
## 物理出口

- Android:每个出站 TCP/UDP socket 在 connect 前调用宿主 protect;失败则当前连接失败关闭。
- Windows:Provider 为当前会话选择不可变的物理网络绑定,并把每个地址族的源 IP 和接口索引交给 Session Host。普通出站 socket 必须同时绑定源地址和 WinSock 接口索引。
- Windows:Provider 为当前会话选择不可变的物理网络绑定;每个地址族只从非 link-local 地址中选择一个源 IP 和接口索引交给 Session Host,同时独立保留物理适配器全部去重的 on-link prefixes(包括 link-local)用于 VPN 路由。普通出站 socket 必须同时绑定源地址和 WinSock 接口索引。
- Windows 只有配置中显式使用 `127.0.0.0/8` 范围内的 IPv4 字面量或 `::1` 的本地出站可以跳过物理绑定;物理代理服务器的域名解析到任何回环地址都会失败关闭。
- 物理适配器、地址或网络身份变化后,Provider 等待 2 秒消抖并停止会话,不迁移 socket 或自动回退。
- 物理适配器、选定源地址、全部 on-link prefixes 或网络身份变化后,Provider 等待 2 秒消抖并停止会话,不迁移 socket 或自动回退。

主机测试只能证明帧、所有权、队列和生命周期逻辑;平台实测范围见 [验收矩阵](acceptance.md)。
Loading
Loading