golang 实现虚拟网卡

golang 实现虚拟网卡

无咎 53 2023-10-19

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 进入 设备管理器,在 网络适配器 中查找是否多出如下驱动

image-eoim.png

若存在驱动,再在控制面板可发现多出一个网络设备

image-mzqx.png

此时即可通过 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 查看方法

image-qgpv.png

image-mysh.png


若正常吊起,可在控制面板看到

image-kfxg.png

同时,在 cmd 中输入 ipconfig 也可发现多出一个网卡信息:
image-vzlg.png

此时,即可开始编写程序令所有流量均走虚拟网卡