wireguard源码分析(八)

package tunnel

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"golang.org/x/sys/windows"
	"golang.zx2c4.com/wireguard/windows/conf"
	"golang.zx2c4.com/wireguard/windows/driver"
	"golang.zx2c4.com/wireguard/windows/services"
	"golang.zx2c4.com/wireguard/windows/tunnel/firewall"
	"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)

type interfaceWatcherError struct {
	serviceError services.Error
	err          error
}

type interfaceWatcherEvent struct {
	luid   winipcfg.LUID
	family winipcfg.AddressFamily
}

type interfaceWatcher struct {
	errors  chan interfaceWatcherError
	started chan winipcfg.AddressFamily

	conf    *conf.Config
	adapter *driver.Adapter
	luid    winipcfg.LUID

	setupMutex              sync.Mutex
	interfaceChangeCallback winipcfg.ChangeCallback
	changeCallbacks4        []winipcfg.ChangeCallback
	changeCallbacks6        []winipcfg.ChangeCallback
	storedEvents            []interfaceWatcherEvent
	watchdog                *time.Timer
}

func (iw *interfaceWatcher) setup(family winipcfg.AddressFamily) {
	iw.watchdog.Stop()
	var changeCallbacks *[]winipcfg.ChangeCallback
	var ipversion string
	if family == windows.AF_INET {
		changeCallbacks = &iw.changeCallbacks4
		ipversion = "v4"
	} else if family == windows.AF_INET6 {
		changeCallbacks = &iw.changeCallbacks6
		ipversion = "v6"
	} else {
		return
	}
	if len(*changeCallbacks) != 0 {
		for _, cb := range *changeCallbacks {
			cb.Unregister()
		}
		*changeCallbacks = nil
	}
	var err error

	if iw.conf.Interface.MTU == 0 {
		log.Printf("Monitoring MTU of default %s routes", ipversion)
		*changeCallbacks, err = monitorMTU(family, iw.luid)
		if err != nil {
			iw.errors <- interfaceWatcherError{services.ErrorMonitorMTUChanges, err}
			return
		}
	}

	log.Printf("Setting device %s addresses", ipversion)
	err = configureInterface(family, iw.conf, iw.luid)
	if err != nil {
		iw.errors <- interfaceWatcherError{services.ErrorSetNetConfig, err}
		return
	}
	evaluateDynamicPitfalls(family, iw.conf, iw.luid)

	iw.started <- family
}

func watchInterface() (*interfaceWatcher, error) {
	iw := &interfaceWatcher{
		errors:  make(chan interfaceWatcherError, 2),
		started: make(chan winipcfg.AddressFamily, 4),
	}
	iw.watchdog = time.AfterFunc(time.Duration(1<<63-1), func() {
		iw.errors <- interfaceWatcherError{services.ErrorCreateNetworkAdapter, errors.New("TCP/IP interface for adapter did not appear after one minute")}
	})
	iw.watchdog.Stop()
	var err error
	iw.interfaceChangeCallback, err = winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
		iw.setupMutex.Lock()
		defer iw.setupMutex.Unlock()

		if notificationType != winipcfg.MibAddInstance {
			return
		}
		if iw.luid == 0 {
			iw.storedEvents = append(iw.storedEvents, interfaceWatcherEvent{iface.InterfaceLUID, iface.Family})
			return
		}
		if iface.InterfaceLUID != iw.luid {
			return
		}
		iw.setup(iface.Family)

		if state, err := iw.adapter.AdapterState(); err == nil && state == driver.AdapterStateDown {
			log.Println("Reinitializing adapter configuration")
			err = iw.adapter.SetConfiguration(iw.conf.ToDriverConfiguration())
			if err != nil {
				log.Println(fmt.Errorf("%v: %w", services.ErrorDeviceSetConfig, err))
			}
			err = iw.adapter.SetAdapterState(driver.AdapterStateUp)
			if err != nil {
				log.Println(fmt.Errorf("%v: %w", services.ErrorDeviceBringUp, err))
			}
		}
	})
	if err != nil {
		return nil, fmt.Errorf("unable to register interface change callback: %w", err)
	}
	return iw, nil
}

func (iw *interfaceWatcher) Configure(adapter *driver.Adapter, conf *conf.Config, luid winipcfg.LUID) {
	iw.setupMutex.Lock()
	defer iw.setupMutex.Unlock()
	iw.watchdog.Reset(time.Minute)

	iw.adapter, iw.conf, iw.luid = adapter, conf, luid
	for _, event := range iw.storedEvents {
		if event.luid == luid {
			iw.setup(event.family)
		}
	}
	iw.storedEvents = nil
}

func (iw *interfaceWatcher) Destroy() {
	iw.setupMutex.Lock()
	iw.watchdog.Stop()
	changeCallbacks4 := iw.changeCallbacks4
	changeCallbacks6 := iw.changeCallbacks6
	interfaceChangeCallback := iw.interfaceChangeCallback
	luid := iw.luid
	iw.setupMutex.Unlock()

	if interfaceChangeCallback != nil {
		interfaceChangeCallback.Unregister()
	}
	for _, cb := range changeCallbacks4 {
		cb.Unregister()
	}
	for _, cb := range changeCallbacks6 {
		cb.Unregister()
	}

	iw.setupMutex.Lock()
	if interfaceChangeCallback == iw.interfaceChangeCallback {
		iw.interfaceChangeCallback = nil
	}
	for len(changeCallbacks4) > 0 && len(iw.changeCallbacks4) > 0 {
		iw.changeCallbacks4 = iw.changeCallbacks4[1:]
		changeCallbacks4 = changeCallbacks4[1:]
	}
	for len(changeCallbacks6) > 0 && len(iw.changeCallbacks6) > 0 {
		iw.changeCallbacks6 = iw.changeCallbacks6[1:]
		changeCallbacks6 = changeCallbacks6[1:]
	}
	firewall.DisableFirewall()
	if luid != 0 && iw.luid == luid {
		// It seems that the Windows networking stack doesn't like it when we destroy interfaces that have active
		// routes, so to be certain, just remove everything before destroying.
		luid.FlushRoutes(windows.AF_INET)
		luid.FlushIPAddresses(windows.AF_INET)
		luid.FlushDNS(windows.AF_INET)
		luid.FlushRoutes(windows.AF_INET6)
		luid.FlushIPAddresses(windows.AF_INET6)
		luid.FlushDNS(windows.AF_INET6)
	}
	iw.setupMutex.Unlock()
}

这段Go代码实现了一个网络接口的监视器 interfaceWatcher,用于监控和管理WireGuard网络适配器在Windows系统中的配置和状态变更。它能够监听网络接口的变化,处理配置的更新,并且在适配器状态改变时进行适当的调整。以下是对这段代码的详细分析。

主要结构体和类型

  1. interfaceWatcherError:

    • 包含两个字段:
      • serviceError:表示服务相关的错误类型。
      • err:具体的错误信息。
  2. interfaceWatcherEvent:

    • 包含两个字段:
      • luid:表示接口的唯一标识符(LUID)。
      • family:表示地址族(IPv4 或 IPv6)。
  3. interfaceWatcher:

    • 主要的监视器结构体,包含如下字段:
      • errors:用于传递错误的通道。
      • started:用于标记启动的通道。
      • conf:存储接口的配置信息。
      • adapter:表示WireGuard驱动的适配器。
      • luid:接口的LUID。
      • setupMutex:互斥锁,确保配置操作的线程安全。
      • interfaceChangeCallback:接口变化的回调函数。
      • changeCallbacks4changeCallbacks6:分别存储IPv4和IPv6的变更回调。
      • storedEvents:存储发生的接口变更事件,等待处理。
      • watchdog:用于超时检测的定时器。

主要函数

  1. setup

    • 负责初始化或重新配置指定地址族(IPv4或IPv6)的网络接口。
    • 停止 watchdog 定时器,防止超时。
    • 清除并重新注册变更回调。
    • 设置MTU(最大传输单元)和接口的IP地址。
    • 评估动态配置的潜在问题(例如动态IP分配)。
  2. watchInterface

    • 创建并初始化一个 interfaceWatcher 实例。
    • 注册接口变更的回调函数 interfaceChangeCallback
    • 在适配器状态发生变化时,调用 setup 函数进行配置。
    • 如果注册回调失败,则返回错误信息。
  3. Configure

    • 配置接口监视器,更新监视器的适配器、配置和LUID。
    • 在设置完这些值后,会重新触发之前存储的事件并进行处理。
    • 重置 watchdog 定时器为1分钟,用于监测配置超时。
  4. Destroy

    • 销毁 interfaceWatcher 实例,清理相关的回调和状态。
    • 停止 watchdog 定时器。
    • 注销所有的变更回调函数。
    • 清理防火墙配置。
    • 删除接口上的所有路由、IP地址和DNS设置,确保接口被安全地销毁。

代码逻辑分析

  • 事件驱动的配置管理interfaceWatcher 通过监听网络接口的变化来动态配置和调整WireGuard适配器。回调函数 interfaceChangeCallback 是事件驱动的核心,它捕捉到接口的添加事件(MibAddInstance),然后触发相关的配置操作。

  • 线程安全:使用 sync.Mutex 进行锁定,确保 setupConfigureDestroy 函数在多线程环境中不会发生竞态条件。

  • 错误处理和监控:通过 errors 通道传递错误,watchdog 定时器监控接口配置的超时情况。如果超时,watchdog 会触发一个错误事件。

  • 资源清理Destroy 函数非常注意资源的清理,尤其是在处理Windows网络堆栈时,确保所有的路由、IP地址和DNS设置被移除,以避免潜在的问题。

代码的应用场景

这段代码主要应用于管理和维护WireGuard的网络适配器配置,确保适配器在各种网络变化(如接口添加、状态变更)时能够自动适应,并且在不需要时可以安全地销毁和清理。它在需要高可靠性和自动化网络配置管理的环境中非常有用,如在Windows系统上运行WireGuard的VPN服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值