"让位于世界两端的计算机,以为自己连在同一根网线上。"
这是一个基于 Linux TAP 虚拟网络设备 和 UDP 封装技术 的轻量级二层 VPN 实现。本项目旨在解决复杂网络环境下的内网穿透与 IP 冲突问题,通过在云端实现一个 Software Defined Switch (软件定义交换机),构建一个跨越物理边界的虚拟二层局域网。
本项目的灵感来源于大学计算机网络课程中的经典实验——《VLAN 划分与三层交换》。 在实验中,我们通过配置三层交换机的路由表和 VLAN 网关,让不同网段的 PC 实现了互联。这种物理层面的网络构建既直观又迷人。
(图 1: 传统实验中的物理拓扑与 VLAN 隔离激发了本项目的设计灵感)
然而,当我试图将实验原理应用到真实互联网时,遇到了无法逾越的物理障碍:
- IP 地址冲突:绝大多数家庭和企业局域网都默认使用了
192.168.1.x网段。如果你想把两个地点的局域网连起来,IP 冲突会导致路由表彻底瘫痪。 - NAT 高墙:由于 IPv4 地址枯竭,几乎所有终端都躲在 NAT(网络地址转换)防火墙后面,外网无法主动发起连接。
- 三层路由限制:公网路由器只负责转发公网 IP,它看不懂也不关心你内网的以太网帧。
为了打破这些物理限制,我决定下沉到 OSI 模型的第二层(数据链路层),用软件模拟一条“云端网线”。
本项目通过 Overlay Network(覆盖网络)技术,实现了以下特性:
- 二层透明传输:不仅能转发 IP 数据包,还能转发 ARP、DHCP 等非 IP 协议的以太网帧。
- 零配置 IP 漫游:即使 Client A 和 Client B 在物理上位于不同城市,且都拥有完全相同的本地私有 IP,它们依然可以通过本系统通信,互不干扰。
- 穿透 NAT:利用 UDP 协议的无连接特性,轻松穿透常见的 NAT 设备,无需配置复杂的端口映射。
- 自学习交换机:云端 Python 服务端实现了标准交换机的 MAC 地址学习算法,维护 CAM 表,智能决策是单播转发还是广播泛洪。
- 客户端 (Edge):
C Language+Linux Kernel TAP Driver+Raw Socket - 服务端 (Cloud):
Python 3+SocketServer
当 Client A 想要给 Client B 发送数据时,流程如下:
- 打包:Client A 的内核将数据生成以太网帧,写入虚拟网卡
qwq。C 程序读取该帧,给它套上一个 UDP 头部。 - 运输:UDP 包通过物理网卡发出,经过复杂的互联网路由,到达云端 Server。
- 分拣:Python 服务端拆开 UDP 包,查看内部以太网帧的
Destination MAC。- 如果是新地址:记录源 MAC 和来源 IP。
- 如果是已知地址:查找 MAC 表,找到 Client B 的隧道信息。
- 投递:Server 将包转发给 Client B。Client B 的 C 程序收到后,剥去 UDP 头部,将原始帧写入自己的
qwq。 - 收货:Client B 的内核以为刚才那个数据包是从网线上过来的,正常处理。
封装了 Linux TAP 设备的底层交互逻辑。
- 机制:打开
/dev/net/tun并通过ioctl(TUNSETIFF)注册设备。 - 关键标志:
IFF_TAP:指定设备工作在二层(处理 Ethernet Frame)。IFF_NO_PI:禁用内核附加的 Packet Info 头部,确保读写的是纯净以太网帧。
- 作用:建立用户态程序与内核网络栈的“秘密通道”。
采用 pthread 实现全双工通信,核心结构体 struct Vport 统一管理 TAP 句柄与 UDP Socket。
- 上行线程 (
upstream_thread):- 阻塞读取 TAP 设备数据(捕获内核发出的帧)。
- 通过
sendto将原始帧作为 UDP 载荷发往 Server。
- 下行线程 (
downstream_thread):- 监听 UDP 端口,接收云端数据。
- 通过
write将帧注入 TAP 设备,模拟物理网卡收包,“欺骗”内核进行协议栈处理。
Python 实现的轻量级 UDP 交换机,手动解析前 14 字节以太网头部。
- MAC学习:动态维护
MAC -> (Client_IP, Port)映射表,实时更新主机位置。 - 转发策略:
- 已知单播:查表命中,精确转发。
- 广播 (
ff:ff:ff:ff:ff:ff):向表中除源地址外的所有活跃客户端泛洪。 - 未知单播:为防止网络风暴,对于表中不存在的目标 MAC,策略为直接丢弃 (Drop)。
事实上我们可以使用 GNS3 搭建一个拓扑图进行抓包来验证程序,拓扑图如下:
- 前置准备:Server-VSwitch 和 PC 机均为 Ubuntu Server,需要将代码拷入。
- Server-VSwitch:在服务器端运行脚本监听端口:
python3 vswitch.py 9999
- Ubuntu-PC:编译 C 客户端,并运行配置脚本:
# 编译 gcc -o client tap.c client.c common.c -I. -lpthread # 运行 (需确保 run.sh 已配置正确 IP) sudo sh run.sh
- 验证:Ping 对方虚拟 IP,并使用 tcpdump 抓包验证二层连通性。
如果你和你的小伙伴拥有 Linux 环境以及一台云服务器,可以将这个底层项目应用起来,做一些有意思的实验:
我在项目中附带了一个 chat.py,这是一个基于 Python tkinter 库编写的简陋对话窗口。
- 配置:修改代码中的
ROLE(角色) 以及服务器 IP。 - 运行:在两台通过 VPN 连通的主机上直接运行
python3 chat.py。 - 效果:数据包将通过我们的 UDP 隧道透明传输,你可以体验一下在这个自己亲手搭建的虚拟局域网中聊天的感觉!
