Golang Window TUN 虚拟网卡

10 篇文章 0 订阅

1. wintun

Linux 2.4以后下有一种特殊的虚拟网络设备tun,用户可以直接创建虚拟网卡tun,直接以文件读写方式从设备处读取到网络层数据包(IP数据包),该网卡可以像是真实网卡一样设置IP、配置路由、读写数据,只不过数据的读写由用户编写的程序完成。

Jason A. Donenfeld 基于tun 向Linux社区贡献了WireGuard 用于实现虚拟网络。

为了开发Windows的WireGuard,开发了wintun并且开源,以动态库的方式分发。[3]

2. 下载Wintun

wintun使用C语言开发,以动态库形式分发。

在这里插入图片描述
下载后解压文件,目录如下:
在这里插入图片描述

bin中存放了各个平台版本的动态库,这里只需要根据平台选择合适的动态库

  • amd64: Windows 64位
  • x86: Windows 32位

3. 入门

https://github.com/Trisia/simpletun

3.1 创建虚拟网卡

WireGuard开发了Wintun GO的接口绑定,安装WireGuard GO依赖

go get -u golang.zx2c4.com/wireguard

注意:译后可执行程序需要与 wintun.dll放置于同一个目录

package main

import (
	"golang.zx2c4.com/wireguard/tun"
	"time"
)

func main() {
	ifname := "MyNIC"
	dev, err := tun.CreateTUN(ifname, 0)
	if err != nil {
		panic(err)
	}
	defer dev.Close()
	time.Sleep(time.Second * 30)
}

若运行程序提示,Unable to load library,请检查wintun.dll是否放置于与可执行程序目录下。
在这里插入图片描述

直接运行上面程序会出现该错误

2022/03/30 11:56:20 Failed to create private namespace: 拒绝访问。 (Code 0x00000
005)
2022/03/30 11:56:20 Failed to take device installation mutex: 拒绝访问。 (Code 0
x00000005)
2022/03/30 11:56:20 Failed to create private namespace: 拒绝访问。 (Code 0x00000
005)
2022/03/30 11:56:20 Failed to take device installation mutex: 拒绝访问。 (Code 0
x00000005)
panic: Error creating interface: Access is denied.

这是由于创建TUN网卡需要一定的操作系统权限,这里我们使用Windows管理员的方式打开程序,就可以。
GoLand可以这样设置
在这里插入图片描述

在使用管理员模式运行后,从网络设备管理器这边
在这里插入图片描述
可以看到刚才创建的虚拟网卡。
在这里插入图片描述

3.2 设置网卡IP以及路由

设置网卡IP需要使用到Windows API,我这直接复制了取自 wireguard的部分API wireguard-windows/winipcfg

在这里插入图片描述

到项目中
在这里插入图片描述

在获取到设备之后,强制类型装换获取到LUID,然后根据相应API完成设置。

package main

import (
	"golang.org/x/sys/windows"
	"golang.zx2c4.com/wireguard/tun"
	"net/netip"
	"simpletun/winipcfg"
)

func main() {
	ifname := "MyNIC"
	dev, err := tun.CreateTUN(ifname, 0)
	if err != nil {
		panic(err)
	}
	defer dev.Close()
	// 保存原始设备句柄
	nativeTunDevice := dev.(*tun.NativeTun)

	// 获取LUID用于配置网络
	link := winipcfg.LUID(nativeTunDevice.LUID())

	ip, err := netip.ParsePrefix("10.0.0.77/24")
	if err != nil {
		panic(err)
	}
	err = link.SetIPAddresses([]netip.Prefix{ip})
	if err != nil {
		panic(err)
	}
	// 配置虚拟网段路由
	// err = link.SetRoutes([]*winipcfg.RouteData{
	//	{net.IPNet{IP: ip.Mask(cidrMask), Mask: cidrMask}, m.gateway, 0},
	//})
	time.Sleep(time.Second * 30)
}

程序运行后通过ipconfig 命令可以看到我们配置IP已经成功
在这里插入图片描述

通过router PRINT -v 可以看见也路由也设置成功
在这里插入图片描述

也可以通过ping命令检测

在这里插入图片描述

3.3 数据读写

完成IP和路由设置之后,就可以使用readwrite API读写IP数据包了,例如以读取ICMP报文为例:

package main

import (
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/sys/windows"
	"golang.zx2c4.com/wireguard/tun"
	"log"
	"net/netip"
	"simpletun/winipcfg"
)

func main() {
	ifname := "MyNIC"
	dev, err := tun.CreateTUN(ifname, 0)
	if err != nil {
		panic(err)
	}
	defer dev.Close()
	// 保存原始设备句柄
	nativeTunDevice := dev.(*tun.NativeTun)

	// 获取LUID用于配置网络
	link := winipcfg.LUID(nativeTunDevice.LUID())

	ip, err := netip.ParsePrefix("10.0.0.77/24")
	if err != nil {
		panic(err)
	}
	err = link.SetIPAddresses([]netip.Prefix{ip})
	if err != nil {
		panic(err)
	}

	n := 2048
	buf := make([]byte, n)
	
	// 读取ICMP
	for {
		n = 2048
		n, err = dev.Read(buf, 0)
		if err != nil {
			panic(err)
		}
		const ProtocolICMP = 1
		header, err := ipv4.ParseHeader(buf[:n])
		if err != nil {
			continue
		}
		if header.Protocol == ProtocolICMP {
			log.Println("Src:", header.Src, " dst:", header.Dst)
			msg, _ := icmp.ParseMessage(ProtocolICMP, buf[header.Len:])
			log.Println(">> ICMP:", msg.Type)
			break;
		}
	}
}

程序运行后使用ping 10.0.0.0网段的其他IP
在这里插入图片描述
可以在控制台看到打印:
在这里插入图片描述

4. 常见问题

Win7 32位无法运行

Win7 32运行程序 提示参数错误 Error loading wintun.dll: Unable to load library: the parameter is incorrect.
在这里插入图片描述
该问是由于Win7缺少 KB2533623 补丁。

https://support.microsoft.com/en-us/topic/microsoft-security-advisory-insecure-library-loading-could-allow-remote-code-execution-486ea436-2d47-27e5-6cb9-26ab7230c704

下载安装该补丁后需要重启就可以解决

在这里插入图片描述

目前该下载链接已经失效,此处附带备份的连接地址 https://download.csdn.net/download/q1009020096/87374944

Unable to load library

若运行程序提示,Unable to load library,请检查wintun.dll是否放置于与可执行程序目录下。
在这里插入图片描述

拒绝访问

在启动运行程序后提示 Failed to create private namespace: 拒绝访问。

在这里插入图片描述
该问提示是由于权限不足引起,需要以管理员模式运行程序

参考文献

[1]. kernel . tuntap . https://www.kernel.org/doc/html/latest/networking/tuntap.html
[2]. wikipedia . WireGuard . https://en.wikipedia.org/wiki/WireGuard
[3]. wintun . https://www.wintun.net/
[4]. github . tun2socks issue . tun2socks程序在WIN7x32系统下无法运行 . https://github.com/xjasonlyu/tun2socks/issues/37
[5]. linux虚拟网络接口 —— tun/tap . sven . 2015.8 . https://lishiwen4.github.io/network/virtual-network-interface-tun-and-tap

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是使用golang实现tun虚拟网卡读写的示例代码: ```go package main import ( "fmt" "net" "os" "golang.org/x/sys/unix" ) func main() { // 打开tun虚拟网卡设备 fd, err := unix.Open("/dev/net/tun", os.O_RDWR, 0) if err != nil { fmt.Printf("Error opening tun device: %v\n", err) return } // 设置tun虚拟网卡的名称和类型 ifr := &unix.Ifreq{} copy(ifr.IfrnName[:], []byte("mytun")) ifr.IfruFlags = unix.IFF_TUN | unix.IFF_NO_PI _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.TUNSETIFF, uintptr(unsafe.Pointer(ifr))) if errno != 0 { fmt.Printf("Error setting tun device: %v\n", errno) return } // 获取tun虚拟网卡的IP地址和掩码 addr := &unix.Ifreq{} copy(addr.IfrnName[:], []byte("mytun")) _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCGIFADDR, uintptr(unsafe.Pointer(addr))) if errno != 0 { fmt.Printf("Error getting tun device address: %v\n", errno) return } ip := net.IPv4(addr.IfruAddr[0], addr.IfruAddr[1], addr.IfruAddr[2], addr.IfruAddr[3]) mask := &unix.Ifreq{} copy(mask.IfrnName[:], []byte("mytun")) _, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.SIOCGIFNETMASK, uintptr(unsafe.Pointer(mask))) if errno != 0 { fmt.Printf("Error getting tun device netmask: %v\n", errno) return } netmask := net.IPv4(mask.IfruAddr[0], mask.IfruAddr[1], mask.IfruAddr[2], mask.IfruAddr[3]) fmt.Printf("Tun device IP is %s, netmask is %s\n", ip.String(), netmask.String()) // 读取tun虚拟网卡的数据包 buf := make([]byte, 1500) n, err := unix.Read(fd, buf) if err != nil { fmt.Printf("Error reading from tun device: %v\n", err) return } fmt.Printf("Received %d bytes from tun device: %v\n", n, buf[:n]) // 向tun虚拟网卡发送数据包 n, err = unix.Write(fd, []byte("hello world")) if err != nil { fmt.Printf("Error writing to tun device: %v\n", err) return } fmt.Printf("Sent %d bytes to tun device\n", n) } ``` 在以上示例代码中,我们首先使用`unix.Open()`函数打开tun虚拟网卡设备,然后使用`unix.Syscall()`函数调用`ioctl()`系统调用来设置tun虚拟网卡的名称和类型,以及获取tun虚拟网卡的IP地址和掩码。然后,我们使用`unix.Read()`函数从tun虚拟网卡读取数据包,并使用`unix.Write()`函数向tun虚拟网卡发送数据包。 需要注意的是,以上示例代码仅用于演示如何使用golang实现tun虚拟网卡读写,并不能直接运行。完整的实现应该考虑更多的细节和错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值