解决校园网中单播互通的不同子网间的 LocalSend 发现问题
问题背景#
咱实验室有一台连接着打印机的计算机,我们在这台机器上挂了一个专门注册的 QQ 账号,需要打印文件时把文件发送到这个 QQ 账号上,在打印机计算机上下载下来就行了。
但是吧,像是比较机密文件的话,如果这样过一道别人的服务器感觉不太好,正好咱当时也找到了局域网文件传输工具 LocalSend,遂试了试。
结果我发现,打印机计算机上的 LocalSend 客户端无法发现我笔记本上的 LocalSend 端,反之亦然。我还得到打印机计算机上手动输入笔记本电脑被分配的 IP 地址才能传输文件,略显麻烦。
- 更不提校园网这有线网和无线网给设备分配的全是动态 IP,可能过一段时间就会变。
分析了一下发现:
没有办法互相发现是因为,我的笔记本接入的是校园网无线网络,而打印机计算机接入的是有线网络,两个主机在不同的子网段内,这阻隔了 LocalSend 发出的组播包 (可以参考 LocalSend 协议)。
可以手动输入 IP 地址来指定客户端传输文件,是因为 LocalSend 实际的传输等请求是单播的,而校园网在三层设备上有配置路由转发,所以单播包是可以互通的。
于是咱要解决的问题就是,如何在这种多播 (组播) 隔离但是单播互通的不同局域子网间实现 LocalSend 的发现功能。
1. 问题描述#
Figure 1: 问题示意图。 可以看到 VLAN 0 中的 LocalSend 客户端无法成功发现 VLAN 2 中的 LocalSend 客户端,反之亦然。
LocalSend 客户端采用 UDP 组播来把自己的存在通告给局域网中其他客户端。然而,像校园网这种大型局域网,通常为了管理和减小广播域规模等目的,会将网络划分为多个 VLAN(虚拟局域网),对应多个子网,即使是现实中距离很近的两个设备,也有可能在不同的 VLAN 中。
- 比如我连接到校园网 WiFi 的笔记本电脑和连接有线校园网的实验室打印机电脑,虽然在同一间屋子,但就是处于不同网段的网络中。
不同子网之间的数据转发依赖于第三层路由设备来实现,不幸的是,LocalSend 向224.0.0.x组播地址及应用端口发送的 UDP 报文段是不会被三层设备转发的,而且其 TTL 值为1,Wireshark 抓包如下:
Figure 2: Wireshark 抓包显示 LocalSend 发送的组播 UDP 报文段的 TTL 值为 1。
因此就有了明明两台设备近在咫尺,但是却没法互相发现对方 LocalSend 客户端的尴尬局面 ㄟ( ▔, ▔ )ㄏ。
2. 解决问题#
2.0. 思路#
尽管多播被隔离了,但是办公区校园网在三层配置上是会转发单播包的,我可以通过单播和不同的 VLAN 中的主机进行通信。
一个 LocalSend 客户端在尝试发现局域网内其他客户端时,会发送组播 UDP 包来声明自己的存在,其他客户端收到组播包后会通过单播的 HTTP 请求来在这个客户端上进行注册。因为单播可以跨 VLAN,所以这个注册操作是可以实现的,我可以替 LocalSend 客户端向局域网内的其他 LocalSend 客户端发送注册请求,从而实现跨 VLAN 的发现和注册。
从官方的协议文档可以看到 LocalSend 的通告包和注册请求的负载中都只有端口信息,没有源 IP 信息,客户端在处理到来的请求时实际上是从网络层分组头部获取到源 IP 地址的,因此这个请求必须从 LocalSend 客户端所处的主机上发出。为了实现这点,我可以在每台有 LocalSend 的主机上都额外运行一个工具进程来代发注册请求。
关键的问题来了,这些工具进程怎么知道局域网内其他 LocalSend 客户端的存在呢?其实我可以借助单播传输来实现这些工具进程之间的通信,从而让它们互相交换各自了解的 LocalSend 客户端信息。
为了解决动态 IP 的问题,我可以把其中一个或多个工具进程作为交换节点部署在拥有静态 IP 的服务器上(内网和外网的均可),然后让其他工具进程连接到这些交换节点,当交换过程收敛时,这些工具进程就能互相了解对方所处主机上的 LocalSend 客户端信息了(也让 LocalSend 客户端互相知晓了对方的存在)。
正好最近学了 Go 语言,照着上面这个思路实现下来,LocalSend Switch 这个工具就诞生辣!٩(>௰<)و
- 简单来说 LocalSend Switch 充当的角色就有点类似于 BT 下载中的 Tracker 服务器了,但同时也会帮忙发送单播的注册请求,用于辅助组播隔离、单播允许的局域网子网之间的 LocalSend 客户端互相发现。
2.1. 工作原理#
Figure 3: LocalSend Switch 的工作原理示意图。实线表示的是单播分组的传播路径,虚线表示的是 TCP 逻辑连接;虚线上的箭头对应数据在逻辑上的传播方向。LocalSend 客户端和 Switch 进程的旁边标记了连接端口,只有 VLAN 1 中的 Switch 进程监听了服务端口
7761,其余两个 Switch 进程的均为 OS 分配的临时端口;LocalSend 客户端默认服务端口是53317。
Fig.3 为 LocalSend Switch 的工作原理示意图,展示了单次的客户端信息传播以及注册请求代发的过程。图中,首先10.84.0.0/15网段中10.84.123.223这台主机上的 LocalSend 客户端发送了组播包,通告自己的存在,被同一台机器上的 LocalSend Switch 捕获到,Switch 进程随后将该通告信息通过单播发送 (图中标记为CLIENT ANNOUNCE,传播路径为蓝色) 给它所连接的所有 Switch 节点 (图中只有192.168.232.47:7761这一个)。
- 发送的数据中封装了 LocalSend客户端的 IP 和端口,无论被转发多少次,这部分数据都不会变,指向最初发出这条通告信息的 LocalSend 客户端。
47主机上 Switch 节点接收到通告的客户端信息后,会将该信息转发至它所连接的其他Switch 节点(图中只有10.94.23.114:52341),图中标记为FORWARD ANNOUNCE,传播路径为紫色。因为这台主机上没有 LocalSend 客户端,所以不会有注册请求的代发操作。
114主机上的 Switch 节点接收到通告信息后,会将该信息发送给它所连接的其他所有 Switch 节点(图中没有其他节点了);因为这台主机上有 LocalSend 客户端,所以 Switch 节点随后会向通告信息中携带的 LocalSend 客户端地址 (图中为10.84.123.223:53317) 发送 HTTP(S) 注册请求(图中标记为REGISTER CLIENT,传播路径为棕色),告知对方本地客户端的 IP 和地址 (图中为10.94.23.114:53317),完成注册请求的代发操作。注意这个注册请求是直接由 Switch 发送给 LocalSend 客户端的。
实际上每个 Switch 节点都有这样的转发功能,甚至可以在逻辑上串联或者组成树形、星型、网状、混合等拓扑结构。
3. 更进一步#
解决了 LocalSend 互相发现的问题后,我又考虑并解决了以下几个问题:
- 传输安全性问题。
- 交换信息的环路问题。
- 自启动问题。
注:这节的 "Switch" 均指 LocalSend Switch 工具。
3.0. 传输安全性问题#
Switch 节点间的数据传输在 TCP 连接上进行,默认情况下是明文的,其中主要是 Lo
