170 lines
5.2 KiB
Go
170 lines
5.2 KiB
Go
// Package vnic 提供创建和管理虚拟网络接口卡(Virtual Network Interface Card)的功能
|
||
// 本包基于TUN设备实现,支持Linux和macOS系统,可用于VPN、网络代理、网络模拟等场景
|
||
package vnic
|
||
|
||
import (
|
||
"fmt"
|
||
"net"
|
||
"runtime"
|
||
|
||
"github.com/songgao/water"
|
||
"github.com/vishvananda/netlink"
|
||
)
|
||
|
||
// Config 包含创建虚拟网卡所需的配置参数
|
||
type Config struct {
|
||
IP string // IP地址(包含CIDR格式的子网掩码,例如 "192.168.1.2/24")
|
||
}
|
||
|
||
// VirtualNIC 表示虚拟网卡接口,封装了底层TUN设备的操作
|
||
type VirtualNIC struct {
|
||
iface *water.Interface // 底层TUN设备接口
|
||
name string // 网卡名称
|
||
ipNet *net.IPNet // IP网络配置
|
||
}
|
||
|
||
// New 创建并配置虚拟网卡
|
||
// 该函数完成以下步骤:
|
||
// 1. 解析提供的IP地址和子网掩码
|
||
// 2. 创建TUN设备
|
||
// 3. 配置网络接口参数
|
||
// 4. 设置路由(仅在Linux和macOS系统上)
|
||
// 返回配置好的虚拟网卡实例和可能的错误
|
||
func New(cfg Config) (*VirtualNIC, error) {
|
||
// 解析IP地址和子网掩码
|
||
ip, ipNet, err := net.ParseCIDR(cfg.IP)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("解析IP失败: %w", err)
|
||
}
|
||
|
||
// 创建TUN设备配置
|
||
waterCfg := water.Config{
|
||
DeviceType: water.TUN, // 使用TUN模式(网络层设备)
|
||
}
|
||
// 获取当前操作系统的平台特定参数
|
||
waterCfg.PlatformSpecificParams = getPlatformParams()
|
||
|
||
// 创建TUN设备实例
|
||
iface, err := water.New(waterCfg)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建TUN设备失败: %w", err)
|
||
}
|
||
|
||
// 初始化虚拟网卡结构体
|
||
vnic := &VirtualNIC{
|
||
iface: iface,
|
||
name: iface.Name(), // 获取系统分配的接口名称
|
||
ipNet: ipNet,
|
||
}
|
||
|
||
// 配置网络接口(设置IP地址和启用接口)
|
||
if err := vnic.configure(ip, ipNet.Mask); err != nil {
|
||
iface.Close() // 配置失败时关闭设备,避免资源泄漏
|
||
return nil, err
|
||
}
|
||
|
||
// 设置路由(仅在Linux和macOS系统上)
|
||
if runtime.GOOS != "windows" {
|
||
if err := vnic.addRoute(); err != nil {
|
||
iface.Close() // 添加路由失败时关闭设备
|
||
return nil, fmt.Errorf("添加路由失败: %w", err)
|
||
}
|
||
}
|
||
|
||
return vnic, nil
|
||
}
|
||
|
||
// getPlatformParams 根据不同操作系统返回适合的平台特定参数
|
||
// 不同操作系统对TUN/TAP设备的配置要求不同,此函数处理这些差异
|
||
func getPlatformParams() water.PlatformSpecificParams {
|
||
switch runtime.GOOS {
|
||
case "darwin": // macOS系统
|
||
return water.PlatformSpecificParams{
|
||
Name: "utun", // macOS要求TUN设备名称以"utun"开头
|
||
}
|
||
case "linux": // Linux系统
|
||
return water.PlatformSpecificParams{
|
||
// 可以在这里设置Linux特定的参数
|
||
// Permissions: &water.DevicePermissions{0666}, // 设置设备权限
|
||
}
|
||
default: // 其他操作系统
|
||
return water.PlatformSpecificParams{}
|
||
}
|
||
}
|
||
|
||
// configure 配置网络接口的IP地址和状态
|
||
// 该方法完成以下配置:
|
||
// 1. 获取网络接口的引用
|
||
// 2. 设置接口的IP地址和子网掩码
|
||
// 3. 启用网络接口
|
||
func (v *VirtualNIC) configure(ip net.IP, mask net.IPMask) error {
|
||
// 通过接口名称获取网络接口引用
|
||
link, err := netlink.LinkByName(v.name)
|
||
if err != nil {
|
||
return fmt.Errorf("获取网络接口失败: %w", err)
|
||
}
|
||
|
||
// 设置IP地址和子网掩码
|
||
addr := &netlink.Addr{IPNet: &net.IPNet{IP: ip, Mask: mask}}
|
||
if err := netlink.AddrAdd(link, addr); err != nil {
|
||
return fmt.Errorf("添加IP地址失败: %w", err)
|
||
}
|
||
|
||
// 启用网络接口(相当于 ip link set up)
|
||
if err := netlink.LinkSetUp(link); err != nil {
|
||
return fmt.Errorf("启用接口失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// addRoute 为虚拟网卡添加路由配置(仅支持Linux/macOS)
|
||
// 添加一个本地路由,使得发往虚拟网卡IP网段的数据包能够正确路由
|
||
func (v *VirtualNIC) addRoute() error {
|
||
_, dst, _ := net.ParseCIDR(v.ipNet.String())
|
||
route := netlink.Route{
|
||
LinkIndex: v.index(), // 设置路由的网络接口索引
|
||
Dst: dst, // 目标网段
|
||
Src: v.ipNet.IP, // 源IP地址
|
||
Scope: netlink.SCOPE_LINK, // 设置为链路本地范围
|
||
}
|
||
return netlink.RouteAdd(&route)
|
||
}
|
||
|
||
// index 获取网络接口的系统索引号
|
||
// 如果接口不存在则返回-1
|
||
func (v *VirtualNIC) index() int {
|
||
if v.iface == nil {
|
||
return -1
|
||
}
|
||
name := v.Name()
|
||
ifx, _ := net.InterfaceByName(name)
|
||
return ifx.Index
|
||
}
|
||
|
||
// Close 关闭虚拟网卡并释放相关资源
|
||
// 在不再使用虚拟网卡时必须调用此方法以避免资源泄漏
|
||
func (v *VirtualNIC) Close() error {
|
||
return v.iface.Close()
|
||
}
|
||
|
||
// Name 返回虚拟网卡的接口名称
|
||
// 该名称是系统分配的,在Linux上通常类似于"tun0",在macOS上类似于"utun1"
|
||
func (v *VirtualNIC) Name() string {
|
||
return v.name
|
||
}
|
||
|
||
// Read 从虚拟网卡读取数据包
|
||
// 参数p用于存储读取的数据,返回读取的字节数和可能的错误
|
||
// 这个方法会阻塞直到有数据可读或发生错误
|
||
func (v *VirtualNIC) Read(p []byte) (int, error) {
|
||
return v.iface.Read(p)
|
||
}
|
||
|
||
// Write 向虚拟网卡写入数据包
|
||
// 参数p包含要写入的数据,返回写入的字节数和可能的错误
|
||
// 写入的数据应该是完整的IP数据包
|
||
func (v *VirtualNIC) Write(p []byte) (int, error) {
|
||
return v.iface.Write(p)
|
||
}
|