新增虚拟网卡(vnic)核心功能及示例程序
- 实现虚拟网卡创建、配置和管理功能 - 添加基础示例(basic)展示基本用法 - 添加完整示例(complete)支持命令行参数和详细日志 - 添加.gitignore忽略vendor目录 - 添加go.mod和go.sum管理依赖
This commit is contained in:
commit
b3a2a5892e
|
@ -0,0 +1 @@
|
|||
vendor/
|
|
@ -0,0 +1,91 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.kingecg.top/kingecg/vnic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建虚拟网卡配置
|
||||
config := vnic.Config{
|
||||
IP: "192.168.100.1/24", // 设置虚拟网卡的IP地址和子网掩码
|
||||
}
|
||||
|
||||
// 创建虚拟网卡
|
||||
log.Println("正在创建虚拟网卡...")
|
||||
nic, err := vnic.New(config)
|
||||
if err != nil {
|
||||
log.Fatalf("创建虚拟网卡失败: %v", err)
|
||||
}
|
||||
|
||||
// 确保程序退出时关闭虚拟网卡
|
||||
defer func() {
|
||||
log.Println("正在关闭虚拟网卡...")
|
||||
if err := nic.Close(); err != nil {
|
||||
log.Printf("关闭虚拟网卡时出错: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("虚拟网卡创建成功,接口名称: %s", nic.Name())
|
||||
|
||||
// 创建一个通道来监听系统信号
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// 启动一个goroutine来读取数据包
|
||||
go func() {
|
||||
buffer := make([]byte, 2000)
|
||||
for {
|
||||
n, err := nic.Read(buffer)
|
||||
if err != nil {
|
||||
log.Printf("读取数据包时出错: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("收到数据包: %d 字节", n)
|
||||
|
||||
// 在实际应用中,这里可以解析和处理IP数据包
|
||||
// 例如:解析IP头部、提取源/目标地址等
|
||||
}
|
||||
}()
|
||||
|
||||
// 每隔5秒发送一个测试数据包
|
||||
go func() {
|
||||
// 创建一个简单的ICMP Echo请求数据包(ping)
|
||||
// 注意:这只是一个示例,实际上这个数据包格式不完整
|
||||
icmpPacket := []byte{
|
||||
0x45, 0x00, 0x00, 0x1c, // IP头: 版本、服务类型、总长度
|
||||
0x12, 0x34, 0x00, 0x00, // IP头: 标识、标志、片偏移
|
||||
0x40, 0x01, 0x00, 0x00, // IP头: TTL、协议(ICMP=1)、校验和
|
||||
0xc0, 0xa8, 0x64, 0x01, // IP头: 源IP (192.168.100.1)
|
||||
0xc0, 0xa8, 0x64, 0x02, // IP头: 目标IP (192.168.100.2)
|
||||
|
||||
0x08, 0x00, 0x00, 0x00, // ICMP: 类型(Echo请求)、代码、校验和
|
||||
0x00, 0x01, 0x00, 0x01, // ICMP: 标识符、序列号
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
_, err := nic.Write(icmpPacket)
|
||||
if err != nil {
|
||||
log.Printf("发送数据包时出错: %v", err)
|
||||
} else {
|
||||
log.Println("已发送测试数据包")
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待中断信号
|
||||
<-sigCh
|
||||
fmt.Println("\n收到中断信号,程序退出")
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.kingecg.top/kingecg/vnic"
|
||||
)
|
||||
|
||||
// 命令行参数
|
||||
var (
|
||||
ipAddress string
|
||||
packetSize int
|
||||
interval int
|
||||
verbose bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 解析命令行参数
|
||||
flag.StringVar(&ipAddress, "ip", "192.168.100.1/24", "虚拟网卡的IP地址 (CIDR格式)")
|
||||
flag.IntVar(&packetSize, "size", 64, "测试数据包大小 (字节)")
|
||||
flag.IntVar(&interval, "interval", 5, "发送数据包的间隔 (秒)")
|
||||
flag.BoolVar(&verbose, "verbose", false, "启用详细日志")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 配置日志格式
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||
log.Println("虚拟网卡完整示例启动")
|
||||
|
||||
// 验证IP地址格式
|
||||
if _, _, err := net.ParseCIDR(ipAddress); err != nil {
|
||||
log.Fatalf("无效的IP地址格式: %v", err)
|
||||
}
|
||||
|
||||
// 创建虚拟网卡配置
|
||||
config := vnic.Config{
|
||||
IP: ipAddress,
|
||||
}
|
||||
|
||||
// 创建虚拟网卡
|
||||
log.Printf("正在创建虚拟网卡 (IP: %s)...", ipAddress)
|
||||
nic, err := vnic.New(config)
|
||||
if err != nil {
|
||||
log.Fatalf("创建虚拟网卡失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置清理函数
|
||||
defer cleanupResources(nic)
|
||||
|
||||
log.Printf("虚拟网卡创建成功:")
|
||||
log.Printf("- 接口名称: %s", nic.Name())
|
||||
log.Printf("- IP地址: %s", ipAddress)
|
||||
|
||||
// 创建一个通道来监听系统信号
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// 启动数据包处理器
|
||||
stopCh := make(chan struct{})
|
||||
go packetReceiver(nic, stopCh)
|
||||
go packetSender(nic, stopCh)
|
||||
|
||||
// 等待中断信号
|
||||
sig := <-sigCh
|
||||
fmt.Printf("\n收到信号 %v,正在关闭...\n", sig)
|
||||
|
||||
// 通知所有goroutine停止
|
||||
close(stopCh)
|
||||
|
||||
// 给goroutine一些时间来完成清理
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// cleanupResources 负责清理资源
|
||||
func cleanupResources(nic *vnic.VirtualNIC) {
|
||||
log.Println("正在关闭虚拟网卡...")
|
||||
if err := nic.Close(); err != nil {
|
||||
log.Printf("关闭虚拟网卡时出错: %v", err)
|
||||
} else {
|
||||
log.Println("虚拟网卡已成功关闭")
|
||||
}
|
||||
}
|
||||
|
||||
// packetReceiver 从虚拟网卡读取数据包
|
||||
func packetReceiver(nic *vnic.VirtualNIC, stopCh <-chan struct{}) {
|
||||
log.Println("数据包接收器已启动")
|
||||
|
||||
// 创建缓冲区
|
||||
buffer := make([]byte, 2048)
|
||||
|
||||
for {
|
||||
// 检查是否收到停止信号
|
||||
select {
|
||||
case <-stopCh:
|
||||
log.Println("数据包接收器正在关闭")
|
||||
return
|
||||
default:
|
||||
// 设置读取超时,以便我们可以定期检查停止信号
|
||||
// 注意:在实际应用中,你可能需要使用更复杂的方法来处理这个问题
|
||||
nic.Read(buffer)
|
||||
|
||||
// 解析IP数据包
|
||||
if verbose && len(buffer) > 0 {
|
||||
parseIPPacket(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// packetSender 定期向虚拟网卡发送测试数据包
|
||||
func packetSender(nic *vnic.VirtualNIC, stopCh <-chan struct{}) {
|
||||
log.Println("数据包发送器已启动")
|
||||
|
||||
// 解析IP地址以获取网络信息
|
||||
ip, ipNet, _ := net.ParseCIDR(ipAddress)
|
||||
|
||||
// 计算网络中的第二个IP地址作为目标
|
||||
// 这只是一个示例,在实际应用中,你可能需要一个有效的目标IP
|
||||
dstIP := calculateNextIP(ip, ipNet)
|
||||
|
||||
// 创建测试数据包
|
||||
packet := createTestPacket(ip, dstIP, packetSize)
|
||||
|
||||
// 设置定时器
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
log.Println("数据包发送器正在关闭")
|
||||
return
|
||||
case <-ticker.C:
|
||||
if _, err := nic.Write(packet); err != nil {
|
||||
log.Printf("发送数据包时出错: %v", err)
|
||||
} else if verbose {
|
||||
log.Printf("已发送 %d 字节的测试数据包到 %s", len(packet), dstIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculateNextIP 计算网络中的下一个IP地址
|
||||
func calculateNextIP(ip net.IP, ipNet *net.IPNet) net.IP {
|
||||
// 复制IP以避免修改原始IP
|
||||
nextIP := make(net.IP, len(ip))
|
||||
copy(nextIP, ip)
|
||||
|
||||
// 将最后一个字节加1
|
||||
nextIP[len(nextIP)-1]++
|
||||
|
||||
// 确保IP仍然在网络范围内
|
||||
if !ipNet.Contains(nextIP) {
|
||||
// 如果超出范围,使用网络的第一个可用IP
|
||||
copy(nextIP, ipNet.IP)
|
||||
nextIP[len(nextIP)-1]++
|
||||
}
|
||||
|
||||
return nextIP
|
||||
}
|
||||
|
||||
// createTestPacket 创建一个测试IP数据包
|
||||
func createTestPacket(srcIP, dstIP net.IP, size int) []byte {
|
||||
// 确保最小大小
|
||||
if size < 20 {
|
||||
size = 20 // IP头部的最小大小
|
||||
}
|
||||
|
||||
// 创建一个基本的IP头部
|
||||
packet := make([]byte, size)
|
||||
|
||||
// IP版本(4)和头部长度(5*4=20字节)
|
||||
packet[0] = 0x45
|
||||
|
||||
// 服务类型
|
||||
packet[1] = 0x00
|
||||
|
||||
// 总长度 (大端字节序)
|
||||
packet[2] = byte(size >> 8)
|
||||
packet[3] = byte(size)
|
||||
|
||||
// 标识
|
||||
packet[4] = 0x00
|
||||
packet[5] = 0x00
|
||||
|
||||
// 标志和片偏移
|
||||
packet[6] = 0x00
|
||||
packet[7] = 0x00
|
||||
|
||||
// TTL
|
||||
packet[8] = 64
|
||||
|
||||
// 协议 (1 = ICMP)
|
||||
packet[9] = 0x01
|
||||
|
||||
// 头部校验和 (暂时为0)
|
||||
packet[10] = 0x00
|
||||
packet[11] = 0x00
|
||||
|
||||
// 源IP地址
|
||||
copy(packet[12:16], srcIP.To4())
|
||||
|
||||
// 目标IP地址
|
||||
copy(packet[16:20], dstIP.To4())
|
||||
|
||||
// 计算IP头部校验和
|
||||
checksum := calculateIPChecksum(packet[:20])
|
||||
packet[10] = byte(checksum >> 8)
|
||||
packet[11] = byte(checksum)
|
||||
|
||||
// 如果有足够的空间,添加一些ICMP数据
|
||||
if size >= 28 {
|
||||
// ICMP Echo请求
|
||||
packet[20] = 0x08 // 类型: Echo
|
||||
packet[21] = 0x00 // 代码: 0
|
||||
packet[22] = 0x00 // 校验和 (暂时为0)
|
||||
packet[23] = 0x00
|
||||
|
||||
// ICMP标识符和序列号
|
||||
packet[24] = 0x00
|
||||
packet[25] = 0x01
|
||||
packet[26] = 0x00
|
||||
packet[27] = 0x01
|
||||
|
||||
// 填充剩余空间
|
||||
for i := 28; i < size; i++ {
|
||||
packet[i] = byte(i % 256)
|
||||
}
|
||||
}
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
// calculateIPChecksum 计算IP头部的校验和
|
||||
func calculateIPChecksum(header []byte) uint16 {
|
||||
var sum uint32
|
||||
|
||||
// 将头部视为16位整数的序列并求和
|
||||
for i := 0; i < len(header); i += 2 {
|
||||
if i+1 < len(header) {
|
||||
sum += uint32(header[i])<<8 | uint32(header[i+1])
|
||||
} else {
|
||||
sum += uint32(header[i]) << 8
|
||||
}
|
||||
}
|
||||
|
||||
// 将进位加到结果中
|
||||
for sum > 0xffff {
|
||||
sum = (sum & 0xffff) + (sum >> 16)
|
||||
}
|
||||
|
||||
// 取反
|
||||
return ^uint16(sum)
|
||||
}
|
||||
|
||||
// parseIPPacket 解析并显示IP数据包的基本信息
|
||||
func parseIPPacket(packet []byte) {
|
||||
if len(packet) < 20 {
|
||||
log.Println("收到无效的IP数据包 (太短)")
|
||||
return
|
||||
}
|
||||
|
||||
// 提取版本和头部长度
|
||||
version := packet[0] >> 4
|
||||
headerLen := int(packet[0]&0x0F) * 4
|
||||
|
||||
if version != 4 {
|
||||
log.Printf("收到非IPv4数据包 (版本: %d)", version)
|
||||
return
|
||||
}
|
||||
|
||||
// 提取总长度
|
||||
totalLen := int(packet[2])<<8 | int(packet[3])
|
||||
|
||||
// 提取协议
|
||||
protocol := packet[9]
|
||||
|
||||
// 提取源IP和目标IP
|
||||
srcIP := net.IPv4(packet[12], packet[13], packet[14], packet[15])
|
||||
dstIP := net.IPv4(packet[16], packet[17], packet[18], packet[19])
|
||||
|
||||
// 显示基本信息
|
||||
log.Printf("收到IP数据包: 长度=%d, 协议=%d, %s -> %s",
|
||||
totalLen, protocol, srcIP, dstIP)
|
||||
|
||||
// 如果是ICMP协议,显示更多信息
|
||||
if protocol == 1 && len(packet) >= headerLen+2 {
|
||||
icmpType := packet[headerLen]
|
||||
icmpCode := packet[headerLen+1]
|
||||
log.Printf(" ICMP: 类型=%d, 代码=%d", icmpType, icmpCode)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
module git.kingecg.top/kingecg/vnic
|
||||
|
||||
go 1.23.1
|
||||
|
||||
require github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||
|
||||
require github.com/vishvananda/netns v0.0.5 // indirect
|
||||
|
||||
require (
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
||||
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
|
||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
@ -0,0 +1,169 @@
|
|||
// 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)
|
||||
}
|
Loading…
Reference in New Issue