第1篇:我为什么要花 3000 个小时写一个轮子

📅 2026/6/30 23:37:40
第1篇:我为什么要花 3000 个小时写一个轮子
第1篇我为什么要花 3000 个小时写一个轮子一、一个歪歪扭扭的书架我有个发小前几年突然迷上了木工。他买了一套刨子、锯子、凿子在阳台上搭了一个工作台每天下班回来就锯木头。三个月后他发给我一张照片——一个歪歪扭扭的书架左边比右边高三厘米搁板之间的间距肉眼可见地不均匀。他说他老婆看了一眼说挺好的放地下室吧。我问他为什么要自己做一个书架宜家 199 块钱就能买一个比这好看十倍的。他说“去宜家买一个书架只需要二十分钟但你不会在二十年之后还记得那二十分钟。这个书架虽然丑但我记得每一块木板是怎么锯的、每一颗螺丝是怎么拧的。”写 wifi2socks 这件事跟他做书架差不多。市面上不缺软路由。OpenWrt 是一个伟大的项目它把 Linux 塞进了一个几十块钱的路由器里给你一个完整的包管理系统。RouterOS 也很强MikroTik 的硬件便宜到令人发指功能多到令人发指。ikuai、高恪、各类定制版本……它们各有各的好。但它们都有一个共同的问题你得折腾。你得刷机、你得配 VLAN、你得理解旁路由和主路由的区别、你得在技术论坛翻几十页帖子找一个能用的固件然后祈祷 md5 是对的。你在配置防火墙规则的时候手一抖就只能抱着路由器去捅 reset 孔。我折腾过所有这些。折腾完之后我想能不能有一个软路由让完全不懂网络的人也能用就像你买个无线路由器回家插上电手机连上 WiFi密码输一下——就这些没了。什么 VLAN、什么旁路由、什么uci commit——统统不存在的。这就是 wifi2socks 的设计目标。四步走把程序复制到 Windows 电脑上 → 双击运行 → 手机连它发射的 WiFi → 上网。没了。当然如果你想要高级功能——自定义路由策略、智能选路、DNS 安全解析——这些也都在。但默认配置下它就是能跑。二、为什么挑了 Windows 这棵树上吊说到这里一定有人要问为什么是 Windows先说最直接的原因大部分人用的就是 Windows。你家里那台常年不关机的台式机是 Windows你插着网线的笔记本是 Windows你公司里日常办公的电脑也是 Windows。如果软路由能直接跑在上面你不需要额外买一个软路由硬件你的电脑本身就是软路由。还有一个原因说出来可能让 Linux 用户不高兴在 Linux 上用一堆开源软件拼拼凑凑就能达到 wifi2socks 的功能。hostapd创建热点iptables做 NAT 和流量管理——几个工具、几行配置软路由就有了。但 Windows 上没有这套现成的组合。你想在 Windows 上用到这些能力不存在的。Windows 的用户面对的是另一个现实要么装个虚拟机跑 OpenWrt然后每次开机都要等虚拟机启动要么忍受各种半成品方案——要么自己写一个。我选了第三个。但 Windows 有一个被严重低估的优势它本身就是一个功能齐全的服务器操作系统。它有完整的内核态驱动框架NDIS有高性能的 IOCP 异步 I/O有 WinRT 提供的 WiFi Direct API。只不过大部分人只拿它来打游戏和写 Word从来没想过它还能当路由器。这就好比你家厨房里有一把非常好的刀但你从来没用来切过菜——你只用它拆快递。我现在就是拿着这把刀开始切菜。三、一个数据包的冒险故事在正式开始之前我们先做一次思维实验。假设你的手机连上了 wifi2socks 发射的 WiFi 热点。你打开浏览器输入www.example.com按下回车。接下来的 0.3 秒内发生了什么第一站无线电波。手机把 HTTP 请求包装成 TCP SYN 包交给 WiFi 芯片变成无线电波发射出去。你的电脑的 WiFi 芯片收到解调还原成一串 0 和 1。第二站NDIS 捕获。这串 0 和 1 在 Windows 的网络栈里往上走。在网卡驱动和TCP/IP 协议栈之间有一个叫 NDIS 的地方。我们的 WinPkFilter 驱动就工作在这里。它看到了这个包——目标 IP 是 93.184.216.34example.com目标 MAC 地址是网关也就是我们——把它截获下来交给用户态程序处理。如果让它直接走到 Windows 协议栈Windows 会看一眼目标 IP——“不是我的丢掉”——那后面的工作就没法做了。第三站NAT 查表 路由决策。被捕获的 SYN 包送到用户态。程序提取五元组源 IP、源端口、目标 IP、目标端口、协议去 NAT 管理器里登记——手机 192.168.137.101:45678 发起的连接目标是 example.com:443。记下来一会要用。然后去路由策略引擎里走一趟——“目标 IP 93.184.216.34 应该走哪条路哦走优化通道。”第四站重定向到本地端口。决定走优化通道之后程序不直接去连外部网络。它先做一件事把 SYN 包的目标地址改写成127.0.0.1:3080这是 AppListener 监听的本地端口重新算好校验和然后把包注入回 Windows 协议栈。Windows 看到这个包的目标是本机——“哦有人在连我的 3080 端口。” 正常完成三次握手。手机以为自己连上了 example.com实际上连的是本机 3080。第五站查缓存 建立连接。AppListener 收到这个新连接——“有人连我了但他本来想去哪” 它从连接的源 IP 和源端口算出 NAT key去 NAT 缓存里一查——“哦你本来要去 example.com:443。” 然后 AppListener 发起一次全新的、独立的TCP 连接——这次是通过配置好的线路连到 example.com 所在的服务器。现在有了两条完全独立的 TCP 连接连接 A手机 ↔ 本机 3080 端口手机以为对方是 example.com连接 B本机 ↔ example.com真正的通信链路第六站数据桥接。两条连接都就绪之后程序的唯一工作是——把连接 A 收到的数据送到连接 B把连接 B 收到的数据送到连接 A。就像一个双向水管——这边进那边出那边进那边出。TCP 的超时重传、ACK/SEQ、窗口维护、拥塞控制——全由操作系统内核在两个连接上各自独立维护。程序只碰应用层的数据不碰传输层的状态机。0.3 秒后浏览器显示了 example.com 的页面。你完全不知道刚才有六个关卡在处理你的请求。这就是网络层的透明——用户无感知。四、凌晨三点的 Wireshark写一个软路由最难的不是技术是坚持。技术上的困难当然有。NDIS 驱动的文档稀少得像沙漠里的绿洲WinRT 的 WiFi Direct API 的错误信息让人完全摸不着头脑UDP 数据通道的超时值需要反复测试才能找到合适的——但这些都不是最难的。最难的是那些为什么跑不起来的时刻。比如 DHCP OFFER 发出去了客户端就是不回 REQUEST。协议格式完全正确抓包看 OFFER 确实发出去了。搞了两天发现是 Windows 防火墙在 UDP 67 端口把入站包吃了。比如 WiFi Direct 热点有时候能启动有时候不能Event Log 里只有一句Operation aborted——什么操作为什么 aborted谁决定的微软没告诉你。如果你问我“折腾了三千个小时后悔吗”我会说“后悔不至于。但有很多个凌晨三点我看着 Wireshark 的抓包界面在想我为什么不直接买个软路由刷个 OpenWrt。”然后天亮了bug 修好了包开始正常流转了。那种畅快感——大概跟把一棵歪脖子树修剪直了差不多。不值三千个小时但人生中的大部分快乐本来就不是按小时计价的。五、下一篇今天这篇是开胃菜。下一篇我们要聊的是技术选型——在决定用 WinPkFilter 之前我试过 LSP、WFP、WinDivert、TUN/TAP。有的方案差点把我的系统搞蓝屏有的方案根本捕获不到 WiFi Direct 的流量因为 ICS 在二层就把流量 forward 走了三层的工具根本看不到。最终选了最底层也最硬核的那个方案——不是因为自虐是因为它是唯一能在 Windows 上实现完整网络接管的路子。到时候见。本文是《从0到1编写一个硬核软路由》系列的第一篇。下一篇第2篇Windows上管理流量的N种姿势