golang 实现虚拟网卡
前置知识
TAP(Network TAP)和TUN(Network TUNnel)是两种虚拟网络设备。它们提供了一种软件定义网络的机制,用于在操作系统层面实现网络隔离和通信。
TAP(Network TAP):
TAP设备是一种虚拟以太网适配器,它可以模拟一个物理的以太网接口。
TAP设备工作在OSI模型的第二层,可以接收和发送以太网帧。
TAP设备通常用于虚拟化和网络测试等场景,可以用于创建虚拟机、容器等网络隔离环境。
TUN(Network TUNnel):
TUN设备是一种虚拟网络设备,它在内核层面实现了网络隧道功能。
TUN设备工作在OSI模型的第三层,可以接收和发送IP数据包。
TUN设备通常用于创建VPN(Virtual Private Network)连接、隧道协议(如GRE、IPsec等)以及其他网络隧道技术。
总结起来,TAP设备模拟以太网接口,在第二层工作,适用于创建虚拟网络环境。而TUN设备实现网络隧道功能,在第三层工作,适用于创建VPN连接和隧道协议。这两种设备在网络虚拟化和网络隔离方面起到了重要的作用,并且在Linux系统中可以通过相关的工具和驱动进行配置和使用。
TAP和TUN设备的工作原理可以通过以下两个示例来解释:
TAP设备示例:
假设我们有两台虚拟机A和B,它们需要进行通信并且需要在网络层面进行隔离。为了实现这个目的,我们可以创建两个TAP设备:tapA和tapB,并将它们分别关联到虚拟机A和B中。
当虚拟机A发送一个以太网帧时,操作系统将该帧发送给tapA设备。tapA设备会将以太网帧的数据包提取出来,将数据包通过操作系统内核的网络栈处理,然后将处理后的数据包发送到虚拟机B所关联的tapB设备。虚拟机B会接收到这个数据包并进行处理,实现了虚拟机A和虚拟机B之间的通信。
这样,我们通过创建TAP设备并将其关联到虚拟机中,实现了虚拟机之间的隔离和通信。TAP设备在操作系统中工作在第二层,模拟了一个物理的以太网接口。
TUN设备示例:
假设我们需要在两个网络之间建立一个VPN连接,将两个网络进行安全隔离并进行数据传输。为了实现这个目的,我们可以创建两个TUN设备:tunA和tunB,并在两个网络节点上分别配置这些设备。
当网络节点A上的应用程序发送一个IP数据包时,操作系统将该数据包发送给tunA设备。tunA设备会将IP数据包提取出来,并根据VPN协议的规则进行加密和封装,生成一个新的封装后的数据包。这个封装后的数据包可以在公共网络上进行传输,以达到安全隔离的目的。
在网络节点B上,tunB设备接收到这个封装后的数据包,并将其解封装和解密,还原为原始的IP数据包。然后操作系统将这个IP数据包传递给应用程序进行处理。这样,我们通过创建TUN设备和VPN协议,实现了网络节点A和B之间的安全通信。
这个示例说明了TUN设备在操作系统中工作在第三层,实现了网络隧道功能,可以用于创建VPN连接和隧道协议。它将原始的IP数据包进行封装和解封装,实现了网络节点之间的隔离和通信。
具体实现(golang 需 1.17+)
通过 tap-windows-9.23.3-I601-Win10 安装 TAP-Windows Adapter V9。
完成后 win+R 输入 devmgmt.msc 进入 设备管理器,在 网络适配器 中查找是否多出如下驱动
若存在驱动,再在控制面板可发现多出一个网络设备
此时即可通过 golang 吊起此虚拟网卡,并将所有流量控制到该网卡中,以实现系统的全局代理。
基于 TUN 模式的全局代理
go get -u github.com/songgao/water
go get -u github.com/songgao/water/waterutil
先编写一个 test 查看是否能正确吊起虚拟网卡
package main
import (
"github.com/songgao/packets/ethernet"
"github.com/songgao/water"
"log"
)
func main() {
//temp := water.PlatformSpecificParams{
// ComponentID: "tap0901",
// Network: "192.168.1.10/24",
//}
ifce, err := water.New(water.Config{
DeviceType: water.TUN,
//PlatformSpecificParams: temp,
})
if err != nil {
log.Fatal(err)
}
var frame ethernet.Frame
for {
frame.Resize(1500)
n, err := ifce.Read([]byte(frame))
if err != nil {
log.Fatal(err)
}
frame = frame[:n]
log.Printf("Dst: %s\n", frame.Destination())
log.Printf("Src: %s\n", frame.Source())
log.Printf("Ethertype: % x\n", frame.Ethertype())
log.Printf("Payload: % x\n", frame.Payload())
}
}
注:ComponentID 查看方法
若正常吊起,可在控制面板看到
同时,在 cmd 中输入 ipconfig 也可发现多出一个网卡信息:
此时,即可开始编写程序令所有流量均走虚拟网卡