# PT瞎折腾 --- 最近回家的时候发现家里的 IPv6 已经基本上 full-featured 了,于是想着在自己电脑上也尝试一点 IPv6 的特性。比如 —— PT ## Concepts 众所周知,PT 是一种非常依赖公网 IP 的业务,BT 等众多 P2P 协议设计的早期 IP 地址耗尽情况还没那么严重,那时的理想是网络上各计算机都可以相互连通来传输数据,每个计算机都能获得一对多的网络资源,从而放大共享效率。但这就要求每台计算机都能监听来自任意远程的主动连接,在现在普遍大内网的时代是不现实的。 不过好在 IPv6 的全面铺开逐渐解决了这一点,v6 地址至少杜绝了无法主动接受连接的问题,P2P 的原初理想得以重新获得发挥机会。所以本文虽然标题是 PT 但 **一开始就是为了IPv6 实验** 去的,**并不是一个 PT 教程或推荐方案**,请读者先明确这一点。 ## PoC - Network @ WSL 所用的环境是一台 Windows 台式机,但 Windows 本身并没有什么可捣鼓的。我在家用的电脑是一台 macbook, 下载上传全都能用 aria2 来完成。出于 code hobbyist 的执念,我产生了一个想法:能不能用 WSL 来复刻? ### 桥接 WSL 很快我遇到了第一个问题:我发现 WSL 并不能正常获得 IPv6. 我尝试了升级(`wsl --update`),但最终 [在这个讨论串发现微软不出意外地又一次展示了他的小小震撼][1] 终结了「是我的问题」的错觉。 不过这个讨论里给出了一个方向: **把 WSL 的虚拟交换机改成桥接模式**。 天下苦 WSL2 久矣。除了被人诟病的「虚拟机重型实现」,还有乱飞的私有地址和难以逾越的 NAT 天堑。桥接不就不需要脑控 IP 了,这种好事?! 是的,现在有了。但 **必须在 win11 上才可用**。我赶紧先把 win10 升到了 win11. - 如果你想 follow 我的经验,在正式开始之前我必须提醒你先把 WSL 内所需的各种网络工具都装好(你可以先看一遍所有步骤),否则断网的这段时间你将没有任何临时 workaround, 必须回溯到 **所有步骤之前**。 #### 创建虚拟交换机(Virtual Switch, vSwitch, VMSwitch, 后文用 `VS` 指代) WSL2 启用时会默认创建一个用于 NAT 的虚拟交换机(`vEthernet (WSL)`),同时如果你用 `Hyper-V` 运行虚拟机,还会为其创建默认的 NAT `VS`. 如果你还自定义了一下 `Hyper-V` 虚拟机的网络,那么你的系统里很可能有四五个 `vEthernet(xxx)` ,这种命名方式简直是噩梦,想要记住它们的 IP 更是反人类般地不可能,更何况还是动态的。因此如无必要,我们可以完全用同一个 `VS` 互联所有的虚拟机和 WSL. - 先创建一个 _named `VS`_ : ```powershell PS C:\> New-VMSwitch -Name 'vmbr' -AllowManagementOS $True -NetAdapterName 'BoardIF' #"board interface" PS C:\> Get-NetAdapter Name InterfaceDescription ... ---- -------------------- ... ... vEthernet (vmbr) Hyper-V Virtual Ethernet Adapter #2 ... ``` 这个步骤基本等同于在「_`Hyper-V` 管理器_」中使用「_虚拟交换机管理器_」 创建一个外部交换机。 如果需要更改虚拟机的网络模式,现在可以在虚拟机属性里指定名为 `vmbr` 的 `VS`了。 - 当所有 **非 WSL** 虚拟机都不再使用某个 `VS` 时,可以使用 `Remove-HnsNetwork` 和 `Remove-VMSwitch` 删除它们: ```powershell PS C:\> Get-HnsNetwork | ? type -Like "internal" #参考 help ? ActivityId : 9AF55F4A-873C-4EC1-9E07-C3FD12B55449 AdditionalParams : CurrentEndpointCount : 0 ... PS C:\> Get-HnsNetwork | ? ActivityId -Like "9AF55F4A-873C-4EC1-9E07-C3FD12B55449" | Remove-HnsNetwork PS C:\> Get-VMSwitch SOME_NAME | Remove-VMSwitch ``` #### 使用 WSL 配置文件指定 WSL 使用刚创建的 `VS` - 在当前用户的主目录 (`%USERPROFILE%`, `cd $env:USERPROFILE`)创建 [名为 `.wslconfig` 的文件][2],添加如下行: ```ini [wsl2] # 如果不存在则把该行加到开头 networkingMode = bridged vmSwitch = vmbr # 刚创建的 VS macAddress = 00:15:xx:xx:xx:xx # 为了让路由器能固定 IP,需要指定固定的 MAC 地址 IPv6 = true dhcp = false # 防止 WSL 自动管理 IP 擅自修改 Linux 内的网卡配置 ``` - 重启 WSL (`wsl.exe --shutdown`), 此时在 WSL 内查看 `ip link` 会发现网卡处于不活动状态,且启用后也不会获得 IP,但可以生成 `fe80` 开头的 IPv6 地址,说明 v6 协议栈已经可用了。 #### 为 WSL 启用 systemd 以让 systemd 自动管理网络 **systemd** 支持也是需要升级到 win11 后才可用。在此之前 WSL 的 systemd 方案是用 [genie][3] 之类的模拟环境来运行 systemd 的一些部件。不过现在我们有微软官方实现的 systemd 了。 - 在 WSL 内将如下配置添加到 `/etc/wsl.conf`: ```ini [boot] systemd = true [network] generateResolvConf = false # 将使用 systemd 内建的来代替 hostname = WSL # 方便在路由器上区分与host主机的名字 ``` - 重启 WSL, 将 `/etc/resolv.conf` 重新指回 systemd 的虚拟文件 ```bash $ sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf # 使用resolv.conf 还是 stub-resolv.conf 取决于需求 ``` - [启用systemd 之后会遇到一些问题][4],过程很简单,就不展开了 #### 添加 systemd 使用的网络接口定义 我使用的 WSL 发行版是 `Ubuntu 22.04`, 不同系统使用的 systemd 版本会有区别,请注意参考自己系统环境。 - 在 `/etc/systemd/network/` 下创建新的 `.network` 文件,如 `10-wsl-wired.network`,加入如下内容: ```ini [Match] Name=eth0 [Network] Description=Virtual Switch(vmbr) DHCP=true # 重要 MulticastDNS=true LLDP=true EmitLLDP=true IPv6AcceptRA=true # 重要,用于 IPv6 SLAAC IPv6SendRA=true IPv6PrefixDelegation=static [DHCPv4] CriticalConnection=true ``` - `sudo systemctl restart systemd-networkd` 重启`systemd-networkd` 服务,此时通过 `ip addr show eth0` 命令应该可以看到网卡获得 IPv4 和 IPv6 的地址了。 - 记得修改路由器的 DHCP 配置固定 WSL 环境的 v4 地址。 ### 测试 IPv6 是否互通(重要) 2023 年还在使用的大部分光猫和家用路由器应该都能正确处理 IPv6 的下发,但运营商提供的光猫用作路由器很可能会有其它问题,比如 **阻止 IPv6 入站**。这种问题甚至不是管理员配置的主动措施而是「特性」,所以必须先测试一下互通性来保证 IPv6 是双向完全可用的。 最简单的测试方法是用手机连上移动数据,然后在手机上通过 termux ping 或者开启一个 web server 让手机来访问。或者连上自己的 vps 远程访问回来也行(需要 vps 有 IPv6 地址)。 ### 修改光猫的上网模式 如果你在上一步发现外部的 ICMP 无法发到 WSL 机子上,甚至到不了宿主 Windows 上,那么就需要使用其它设备(如自己的路由器)来拨号了。 我这里光猫的型号是 `tewa-800g`,网上有大量破解并修改存量各种光猫网络模式的各种教程,可以自行寻找。这里放个捷径: 1. 在登录页面打开浏览器控制台,查看 `sessionKey` 变量,例如得到 123 2. 访问 `http://192.168.1.1/loginUser.htm?username=CUAdmin&password=CUAdmin&sessionKey=123` 登录 3. `基本配置` - `上行线路配置` 选择 `INTERNET_` 连接,修改连接模式为 **桥接** 4. 如不知道 PPPoE 拨号密码,可以在路由模式页面中的密码输入框获得(将 input 元素的 type 属性改为 text) ---- 经过以上所有步骤,我们终于完成了第一个目标:现在 WSL 环境获得了固定的内网 IP,也不会再有恼人的端口转发烦恼。你的 Windows 此时真正意义上同时运行着两个系统,它们彼此独立但又很方便地共享你的硬盘和显示器。 最重要的是,现在 「WSL 这台电脑」是有公网 IPv6 的,终于可以开始 PT 的部分了! ---- ## POC - Play PT @ WSL 我一开始尝试了各种客户端。 得益于 win11 的 **_WSLg_** (g for GUI),其实可以直接在 WSL 中安装 Llinux 版的 GUI download manager, 如 `qbittorrent`, `transmission-gui`. 不过这些图形界面版的 BT 工具通常都比 Windows 版简陋,而且无系统托盘、无后台机制,使得他们都不是很好用于挂机。而且说到底想使用 WSL 来挂机就奔着无感的目标去的,用 GUI 版本岂不是南辕北辙?所以最终在观察了各种 peers 的客户端后,我选择了通用方案: `transmission-daemon` ### Transmission `transmission` 本身就是为了使用 BT 协议 **分享** 文件设计的,GUI 版本的功能也都很精简,没有复杂的信息显示和标签化管理,本质上讲只有开始和暂停两个功能,几乎等同于一个 server 软件。 在 Linux 上它还有 headless 的版本 `transmission-daemon`,这就是个实打实的 server 程序了,所有操作必须通过 web 界面和 RPC 来完成。 - 安装: 使用发行版的包管理搜索 `transmission-daemon` - 配置: 1. 首先必须先停止装好后自动启动的 daemon 服务,否则 daemon 停止时会自动回写配置文件:`sudo systemctl stop transmission-daemon` 2. 修改 `/etc/transmission-daemon/settings.json`,注意以下项:`rpc-username` `rpc-password` `rpc-port` 3. 设置好 rpc 后 `sudo systemctl start transmission-daemon` 启动服务,此时可以访问 `http://<WSL-IP>:<RPC-PORT>/transmission/web/` 进行进一步的设置。注意现在 WSL 是与宿主同网段的独立机器了。 - 在 WebUI 上的修改在重启 `transmission-daemon` 服务时保存。 ### 宿主文件系统互通 在 web 界面上传已经下好的种子是个很冗余的操作,可以通过调整文件系统的共享方式来解决它。 #### 挂载用来存文件的大容量硬盘 - [参考官方文档][5]: ```powershell PS C:\> GET-CimInstance -query "SELECT * from Win32_DiskDrive" PS C:\> wsl --mount \\.\PHYSICALDRIVE2 $ sudo mount /dev/sdc3 /mnt/x # WSL 现在可以使用自己内核中的驱动访问物理硬盘 ``` 当然如果你的硬盘文件系统是 NTFS, 可以直接在 _磁盘管理_ 中为其分配盘符,WSL 会自动将其映射到内部。 #### 配置 `transmission-daemon` 文件监视 - 在 `/etc/transmission-daemon/settings.json` 文件中加入如下两项: ```json "watch-dir": "/where/to/save/torrents", "watch-dir-enabled": true ``` 新放入 _watch-dir_ 的种子文件就会自动创建任务了。 不过要注意的是 `transmission-daemon` 使用 `inotify` 来监视文件系统,这意味着它只能监听 Linux 文件系统,尝试监视挂载的 NTFS 卷是不会起作用的。 不过问题不大,下载文件或任何 Windows 自带的文件选择对话框中,在地址栏输入 `\\wsl.localhost\<发行版>` 跳转,都能直接在 Windows 中访问 WSL 的文件系统。可以直接把种子文件下载到监视目录内。 ### 防火墙 由于现在 WSL 环境相当于暴露在公网中,并且使用弱密码还与 Windows 的文件系统互通,所以有必要为其启用防火墙来阻止潜在的攻击。快速简便的方法是直接使用 [_UFW_][6] #### 使用 UFW WSL 的 Ubuntu 发行版已默认安装了 UFW,其它发行版也仅需用自带包管理搜索即可。 - 初次启用 ```bash $ sudo ufw enable $ sudo ufw logging low # 启用日志 $ sudo ufw default reject incoming # 将默认入站策略设为 REJECT ``` 通常来讲默认入站策略会设为 `DROP` ,收到 `SYN` 时直接丢弃,连接的客户端会等到超时。而 `REJECT` 策略会返回一个 ICMP 消息告知客户端服务端主动拒绝连接。由于我的电脑 IPv4 环境处于大局域网内,被扫描器光顾的可能性较低,所以我选择 `REJECT` 策略 ,这样调试本机连接时能方便知道是否被防火墙拦截。 - 添加放行策略 ```bash # 放行高端口段,这部分用于 BT 等可能需要随机分配监听端口的程序 $ sudo ufw allow in on eth0 from any to any port 50000:59999 proto tcp $ sudo ufw allow in on eth0 from any to any port 50000:59999 proto udp # 放行来自局域网的连接(比如宿主Windows) $ sudo ufw allow from 192.168.3.0/24 # web 端口 $ sudo ufw allow 80,443,8000,8001,8080,9000,9001/tcp # ssh (example, 实际应避免使用默认端口) $ sudo ufw allow ssh ``` - 使用 `sudo ufw status [verbose|numbered]` 查看规则表 #### 解决 IPv6 大量包被 BLOCK 的问题 - 启用 ufw 「实时」(其实是更详细的)日志: `sudo ufw logging high` - 查看内核日志会看到大量 BLOCK 记录: `journalctl -k -b | grep BLOCK` ```syslog Feb 08 04:04:36 WSL kernel: [UFW BLOCK] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8207:183d:0120:0211:32ff:fe12:3456 DST=2408:8207:6c62:56c0:0215:5dff:fea7:b951 LEN=140 TC=40 HOPLIMIT=59 FLOWLBL=954762 PROTO=TCP SPT=60954 DPT=50001 WINDOW=234 RES=0x00 ACK PSH FIN URGP=0 Feb 08 04:06:27 WSL kernel: [UFW BLOCK] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8207:183d:0120:0211:32ff:fe12:3456 DST=2408:8207:6c62:56c0:0215:5dff:fea7:b951 LEN=140 TC=40 HOPLIMIT=59 FLOWLBL=553381 PROTO=TCP SPT=37397 DPT=50001 WINDOW=235 RES=0x00 ACK PSH FIN URGP=0 Feb 08 04:06:54 WSL kernel: [UFW BLOCK] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8207:183d:0120:0211:32ff:fe12:3456 DST=2408:8207:6c62:56c0:0215:5dff:fea7:b951 LEN=140 TC=40 HOPLIMIT=59 FLOWLBL=327198 PROTO=TCP SPT=37397 DPT=50001 WINDOW=235 RES=0x00 ACK PSH FIN URGP=0 ``` 这些包的目标端口都是 `transmission` 的监听端口,看起来完全是正常的,也不在规则里,为什么会 BLOCK 呢? - 取消 `grep` 直接 follow 日志观察: `journalctl -k -f` 会发现 `BLOCK` 记录是几乎伴随 `AUDIT INVALID` 记录同时出现的: ```syslog Feb 08 05:33:34 WSL kernel: [UFW AUDIT INVALID] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=240e:0370:051f:b102:0211:32ff:fe22:244d DST=2408:8207:6c62:56c0:0001:0000:0000:0000 LEN=140 TC=0 HOPLIMIT=52 FLOWLBL=915931 PROTO=TCP SPT=55398 DPT=50001 WINDOW=231 RES=0x00 ACK PSH FIN URGP=0 Feb 08 05:33:34 WSL kernel: [UFW BLOCK] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=240e:0370:051f:b102:0211:32ff:fe22:244d DST=2408:8207:6c62:56c0:0001:0000:0000:0000 LEN=140 TC=0 HOPLIMIT=52 FLOWLBL=915931 PROTO=TCP SPT=55398 DPT=50001 WINDOW=231 RES=0x00 ACK PSH FIN URGP=0 ``` - 打开 `/etc/ufw/user6.rules` 会发现如下规则: ```iptables -A ufw6-logging-deny -m conntrack --ctstate INVALID -j LOG --log-prefix "[UFW AUDIT INVALID] " -A ufw6-logging-deny -j LOG --log-prefix "[UFW BLOCK] " ``` 这两条 log 规则会在 _ufw logging level_ > low 的时候被添加,看起来产生 `BLOCK` 记录的原因是包被 `conntrack` 标为无效了。 - 打开 `/etc/ufw/before6.rules` 会发现如下规则: ``` # drop INVALID packets (logs these in loglevel medium and higher) -A ufw6-before-input -m conntrack --ctstate INVALID -j ufw6-logging-deny -A ufw6-before-input -m conntrack --ctstate INVALID -j DROP ``` 被标为 `INVALID` 的包会转到到 `ufw6-logging-deny` 链上记录输出,然后回到 `ufw6-before-input` 链上被 `DROP` 规则丢弃。 由于 `ufw logging` 设为 `high` 之后会有大量 `ALLOW` 和 `AUDIT` 记录影响日志追踪,所以我们可以新建一条链来自定义输出,观察情况。 `/etc/ufw/user[6].rules` 文件会被 ufw 自动管理并修改,但 `before[6].rules` 和 `after[6].rules` 是可以持久化用户自定义的规则的。 - 将自定义链和 LOG 规则加入到 `before6.rules` ``` # custom logging :ufw6-custom-logging - [0:0] -A ufw6-custom-logging -j LOG --log-prefix "[UFW MARKED INVALID] " ``` - 然后修改原有的 `conntrack` 规则 ``` # drop INVALID packets (logs these in loglevel medium and higher) -A ufw6-before-input -m conntrack --ctstate INVALID -j ufw6-custom-logging # -A ufw6-before-input -m conntrack --ctstate INVALID -j ufw6-logging-deny # -A ufw6-before-input -m conntrack --ctstate INVALID -j DROP ``` 然后把 `logging level` 改成 low,这样就只能看到自定义的记录了。 - 观察一段时间后发现会出现同一来源地址短时多次 `INVALID` 的现象: ``` Feb 08 06:41:09 WSL kernel: [UFW MARKED INVALID] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8256:0280:f558:147c:1911:1664:cd28 DST=2408:8207:6c62:56c0:0001:0000:0000:0000 LEN=5580 TC=0 HOPLIMIT=56 FLOWLBL=544976 PROTO=TCP SPT=3197 DPT=50001 WINDOW=1023 RES=0x00 ACK URGP=0 Feb 08 06:41:09 WSL kernel: [UFW MARKED INVALID] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8256:0280:f558:147c:1911:1664:cd28 DST=2408:8207:6c62:56c0:0001:0000:0000:0000 LEN=1440 TC=0 HOPLIMIT=56 FLOWLBL=544976 PROTO=TCP SPT=3197 DPT=50001 WINDOW=1023 RES=0x00 ACK URGP=0 Feb 08 06:41:09 WSL kernel: [UFW MARKED INVALID] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8256:0280:f558:147c:1911:1664:cd28 DST=2408:8207:6c62:56c0:0001:0000:0000:0000 LEN=2820 TC=0 HOPLIMIT=56 FLOWLBL=544976 PROTO=TCP SPT=3197 DPT=50001 WINDOW=1023 RES=0x00 ACK URGP=0 Feb 08 06:41:09 WSL kernel: [UFW MARKED INVALID] IN=eth0 OUT= MAC=00:15:5d:a7:b9:51:68:a0:3e:50:4a:2b:86:dd SRC=2408:8256:0280:f558:147c:1911:1664:cd28 DST=2408:8207:6c62:56c0:0001:0000:0000:0000 LEN=1440 TC=0 HOPLIMIT=56 FLOWLBL=544976 PROTO=TCP SPT=3197 DPT=50001 WINDOW=1023 RES=0x00 ACK URGP=0 ``` - 排查 `conntrack` 表是否打满: `sudo conntrack -L -f IPv6 -p tcp --state TIME_WAIT` 由于 `TIME_WAIT` 等待时间较长且不能积极结束,这是一个通常容易造成资源(即追踪数)耗尽的状态。不过查看之后显示 flow entries 数量保持在 100 以下,完全正常。 那么只能猜测双方 IPv6 的协议栈实现略有区别或非标准行为。由于我用着一个比较旧的路由器,而且是在 WSL 这样一个复杂的网络协议栈环境,所以也难以继续深究,为了防止防火墙阻断本来可以建立的连接,我把 `DROP` 规则和日志记录都禁用了。 ### 终极之路: 公网 IPv4映射+虚拟文件系统实现多点上传 有了公网 IPv6 之后,理论上 PT 的上传就已经可以保证了。但 IPv4 毕竟仍是当前的主流 IP 协议栈,有许多「落后」的用户还不能正确使用 IPv6 的链路相互传输数据,因此挖一个公网 IPv4 的隧道还是有必要的。 传统方法有很多,比如 `ngrok` `frp` 之类的映射工具,或使用 `zerotier` 或 `openvpn` 等建立虚拟局域网,再通过网关上的路由或反代来连接到我们本地的机器。 **但既然我们现在已经有 IPv6 公网 IP 了,为什么还需要我安装额外的客户端再主动反弹一个连接出去呢? 让提供 IPv4 公网的机器主动连接我的 IPv6 地址不就行了吗?** 我的预设方案是一种网络文件系统比如 `NFS` ,IPv4 服务器把我的 IPv6 地址挂载成它的虚拟文件系统,它自己再启动 `transmission` 做种,这样既能获得 IPv4 的上传机会,也能避免在服务器上落盘文件带来的巨额存储开销 #### IPv6 隧道 不过上述方案有一个小缺陷,就是 IPv4 服务器本身必须是双栈的,连接我的 IPv6 地址要求它本身也有 IPv6,并不是所有的 vps 服务商都能提供。 好在它自己有固定的公网 IPv4,可以通过 [免费的 IPv6 隧道服务商][7] 给它创建一条 [SIT 隧道][8],这样它就能与 IPv6 地址互通了。 使用也很简单,注册后左侧 _create regular tunnel_ 按提示选择区域并填入连接隧道时使用的 IPv4 即可。隧道创建好后可在 _Tunnel Details_ 的 _Example Configurations_ 选项卡复制配置方法。配置成功后 `ip addr` 能看到隧道 interface 上获得的公网 IPv6,并且可对本地机器的 IPv6 服务发起连接了。 #### NFS 但当在本地机器上编辑好 `exports` 后会发现并不能成功启用 NFS server: ``` # cat /etc/exports ... /mnt/x/ 2001:x:x:x::2(ro,no_subtree_check,fsid=0) # exportfs -ra exportfs: /mnt/x does not support NFS export ``` 原因是 WSL 挂载外部驱动器或目录时使用的 `drvfs` 文件系统(或 [9p 协议][9])其本身就是一种类 NFS 的网络文件系统,而 NFS 并 **不支持重新导出** 挂载的其它 NFS 文件系统。因此需要寻找其它的基于网络的虚拟文件系统。 #### WebDav/davfs 没错其实这时候本来应该直接奔着 _rclone_ 去就对了,但由于我习惯用 nginx 统一反代整合一下各种 web 服务,所以首先想到的其实是 nginx 的 webdav module. [这里][10] 和 [arch wiki][11] 都讲得很简单清晰,我的步骤也基本照着它们做的。 但我发现 `nginx-dav-ext-module` 有些 [很恶性的 bug][12] ([配合 davfs2 触发][13]), 作者还摆烂了一直不修,(看issue列表和最后更新时间)最后 nginx 模块这个方案否决了。 调试这俩 bug 的过程中发现 golang 实现 webdav server 其实很简单,[顺手也写了一小段][14]。 附 nginx 配置: ```nginx upstream godav { # 使用 unix socket 节省资源也便于服务化 (然而最后并没有实现服务化) server unix:/tmp/dav.sock; server 127.0.0.1:10000; } # ... location /dav/ { set $dest $http_destination; if ($http_destination ~ "^https://(?<path1>(.+))") { set $dest http://$path1; } # rewrite ^\/dav/(.*)$ /$1 break; # 注意上面这个 rewrite 是不起作用的, webdav 的很多操作请求路径包含在 body xml 中,因此只能在 server 脚本中指定 prefix 来匹配这里的 location proxy_pass http://godav; proxy_set_header Connection ""; proxy_set_header Destination $dest; proxy_set_header Host $http_host; proxy_set_header Remote-Host $remote_addr; proxy_pass_header Server; # ... ``` 当然了再强调一次 _rclone_ 其实就是最终的完美方案,但我最后并没有用上。原因是上述这套简易的 _webdav/davfs_ 可以正常跑起来已经验证了设想的「终极之路」,但很可惜「终极之路」本身是个伪命题或伪需求,原因有几点: - IPv4 并不是那么必要。PT 的上传量主要还是来源于下新种时互相传输,当种子下完后再连入的客户端已经很少了。 - 远程服务器上的 `transmission` 不能直接共用本机的 cache,因此它需要重新校验文件区块,这需要极其漫长的时间。 - 在远程服务器校验**或检索**文件区块时会占满本地上传带宽,挤占本地原本的上传量,而且还消耗大量的 vps 流量配额,造成浪费。 ## 结尾和其它 其实我在这台机子上这轮捣鼓的东西还不止上述这些。为了随时更方便地访问,这上面还有 ddns,还有 web 版的文件管理器,又为了双栈访问还给它配置了正常工作的 `certbot` (HTTPS证书)和 cloudflare 代理。 虽说初衷是入坑 PT,目的是实验 IPv6 相关的特性支持,但其实最后的惊喜和经验全在 WSL 上。 WSL 的进化之快是令人惊讶(amazing)的,虽然暂时还需要(只需要)一点点小补丁才能享用接近 native linux installation 的体验,但目前已经几乎感知不到蹩脚的完全不符合 linux 习惯的东西了。除开「网络资源和地域限制」类需求必须使用远程的 vps 外,几乎一切依赖 linux 服务器的非生产事务(尤其是原来选用国内服务商完成的那些)都已经可以用 WSL 完美取代。成本、效率、性能,甚至网络条件都要比国内服务商的 vps 更优。 早在 2019 年「最好的 linux 发行版」称号就已经归属 WSL (调侃)了。 2023 年,我愿将「最好的 vps 服务商」称号也(非官方地)授予 WSL. [1]: https://github.com/microsoft/WSL/discussions/5855 [2]: https://learn.microsoft.com/en-us/Windows/wsl/wsl-config [3]: https://github.com/arkane-systems/genie [4]: https://github.com/microsoft/WSL/issues/8843 [5]:https://wiki.ubuntu.com/UncomplicatedFirewall [6]:https://wiki.ubuntu.com/UncomplicatedFirewall [7]: https://tunnelbroker.net/ [8]: https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels#sit_tunnel [9]: https://en.wikipedia.org/wiki/9P_%28protocol%29 [10]: https://www.filestash.app/2021/12/09/nginx-webdav/ [11]: https://wiki.archlinux.org/title/WebDAV [12]: https://github.com/arut/nginx-dav-ext-module/issues/45 [13]: https://savannah.nongnu.org/bugs/index.php?63771 [14]: https://gist.github.com/pnck/a1ad81e5d3ee25839a46f267ea95aa69