vnic/vnic.go

170 lines
5.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}