wireguard源码分析(十)

package tunnel

import (
	"log"
	"net/netip"
	"strings"
	"unsafe"

	"golang.org/x/sys/windows"
	"golang.org/x/sys/windows/svc/mgr"
	"golang.zx2c4.com/wireguard/windows/conf"
	"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)

func evaluateStaticPitfalls() {
	go func() {
		pitfallDnsCacheDisabled()
		pitfallVirtioNetworkDriver()
	}()
}

func evaluateDynamicPitfalls(family winipcfg.AddressFamily, conf *conf.Config, luid winipcfg.LUID) {
	go func() {
		pitfallWeakHostSend(family, conf, luid)
	}()
}

func pitfallDnsCacheDisabled() {
	scm, err := mgr.Connect()
	if err != nil {
		return
	}
	defer scm.Disconnect()
	svc := mgr.Service{Name: "dnscache"}
	svc.Handle, err = windows.OpenService(scm.Handle, windows.StringToUTF16Ptr(svc.Name), windows.SERVICE_QUERY_CONFIG)
	if err != nil {
		return
	}
	defer svc.Close()
	cfg, err := svc.Config()
	if err != nil {
		return
	}
	if cfg.StartType != mgr.StartDisabled {
		return
	}

	log.Printf("Warning: the %q (dnscache) service is disabled; please re-enable it", cfg.DisplayName)
}

func pitfallVirtioNetworkDriver() {
	var modules []windows.RTL_PROCESS_MODULE_INFORMATION
	for bufferSize := uint32(128 * 1024); ; {
		moduleBuffer := make([]byte, bufferSize)
		err := windows.NtQuerySystemInformation(windows.SystemModuleInformation, unsafe.Pointer(&moduleBuffer[0]), bufferSize, &bufferSize)
		switch err {
		case windows.STATUS_INFO_LENGTH_MISMATCH:
			continue
		case nil:
			break
		default:
			return
		}
		mods := (*windows.RTL_PROCESS_MODULES)(unsafe.Pointer(&moduleBuffer[0]))
		modules = unsafe.Slice(&mods.Modules[0], mods.NumberOfModules)
		break
	}
	for i := range modules {
		if !strings.EqualFold(windows.ByteSliceToString(modules[i].FullPathName[modules[i].OffsetToFileName:]), "netkvm.sys") {
			continue
		}
		driverPath := `\\?\GLOBALROOT` + windows.ByteSliceToString(modules[i].FullPathName[:])
		var zero windows.Handle
		infoSize, err := windows.GetFileVersionInfoSize(driverPath, &zero)
		if err != nil {
			return
		}
		versionInfo := make([]byte, infoSize)
		err = windows.GetFileVersionInfo(driverPath, 0, infoSize, unsafe.Pointer(&versionInfo[0]))
		if err != nil {
			return
		}
		var fixedInfo *windows.VS_FIXEDFILEINFO
		fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
		err = windows.VerQueryValue(unsafe.Pointer(&versionInfo[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen)
		if err != nil {
			return
		}
		const minimumPlausibleVersion = 40 << 48
		const minimumGoodVersion = (100 << 48) | (85 << 32) | (104 << 16) | (20800 << 0)
		version := (uint64(fixedInfo.FileVersionMS) << 32) | uint64(fixedInfo.FileVersionLS)
		if version >= minimumGoodVersion || version < minimumPlausibleVersion {
			return
		}
		log.Println("Warning: the VirtIO network driver (NetKVM) is out of date and may cause known problems; please update to v100.85.104.20800 or later")
		return
	}
}

func pitfallWeakHostSend(family winipcfg.AddressFamily, conf *conf.Config, ourLUID winipcfg.LUID) {
	routingTable, err := winipcfg.GetIPForwardTable2(family)
	if err != nil {
		return
	}
	type endpointRoute struct {
		addr         netip.Addr
		name         string
		lowestMetric uint32
		highestCIDR  uint8
		weakHostSend bool
		finalIsOurs  bool
	}
	endpoints := make([]endpointRoute, 0, len(conf.Peers))
	for _, peer := range conf.Peers {
		addr, err := netip.ParseAddr(peer.Endpoint.Host)
		if err != nil || (addr.Is4() && family != windows.AF_INET) || (addr.Is6() && family != windows.AF_INET6) {
			continue
		}
		endpoints = append(endpoints, endpointRoute{addr: addr, lowestMetric: ^uint32(0)})
	}
	for i := range routingTable {
		var (
			ifrow    *winipcfg.MibIfRow2
			ifacerow *winipcfg.MibIPInterfaceRow
			metric   uint32
		)
		for j := range endpoints {
			r, e := &routingTable[i], &endpoints[j]
			if r.DestinationPrefix.PrefixLength < e.highestCIDR {
				continue
			}
			if !r.DestinationPrefix.Prefix().Contains(e.addr) {
				continue
			}
			if ifrow == nil {
				ifrow, err = r.InterfaceLUID.Interface()
				if err != nil {
					continue
				}
			}
			if ifrow.OperStatus != winipcfg.IfOperStatusUp {
				continue
			}
			if ifacerow == nil {
				ifacerow, err = r.InterfaceLUID.IPInterface(family)
				if err != nil {
					continue
				}
				metric = r.Metric + ifacerow.Metric
			}
			if r.DestinationPrefix.PrefixLength == e.highestCIDR && metric > e.lowestMetric {
				continue
			}
			e.lowestMetric = metric
			e.highestCIDR = r.DestinationPrefix.PrefixLength
			e.finalIsOurs = r.InterfaceLUID == ourLUID
			if !e.finalIsOurs {
				e.name = ifrow.Alias()
				e.weakHostSend = ifacerow.ForwardingEnabled || ifacerow.WeakHostSend
			}
		}
	}
	problematicInterfaces := make(map[string]bool, len(endpoints))
	for _, e := range endpoints {
		if e.weakHostSend && e.finalIsOurs {
			problematicInterfaces[e.name] = true
		}
	}
	for iface := range problematicInterfaces {
		log.Printf("Warning: the %q interface has Forwarding/WeakHostSend enabled, which will cause routing loops", iface)
	}
}

这段Go代码的主要功能是评估和检测与网络配置相关的潜在问题(称为“Pitfalls”),特别是在使用WireGuard隧道时。这些问题包括DNS缓存服务的禁用、VirtIO网络驱动程序的过时版本、以及网络接口配置中的弱主机发送(Weak Host Send)功能。这些检测操作分别由三个函数实现:pitfallDnsCacheDisabledpitfallVirtioNetworkDriverpitfallWeakHostSend,它们分别被evaluateStaticPitfallsevaluateDynamicPitfalls两个函数调用来执行。

代码结构和主要功能

1. evaluateStaticPitfalls

该函数用于检测静态配置中的潜在问题,并在独立的Goroutine中运行两个检测函数。

  • 逻辑:
    • pitfallDnsCacheDisabled() 检查DNS缓存服务是否被禁用。
    • pitfallVirtioNetworkDriver() 检测VirtIO网络驱动程序是否过时。
2. evaluateDynamicPitfalls

这个函数用于检测动态配置中的潜在问题,特别是弱主机发送配置的问题,同样在独立的Goroutine中运行。

  • 逻辑:
    • pitfallWeakHostSend(family, conf, luid) 检测与指定LUID对应的网络接口是否存在弱主机发送配置的问题。
3. pitfallDnsCacheDisabled

这个函数检查Windows系统的DNS缓存服务是否被禁用,如果被禁用,则记录警告日志。

  • 逻辑:
    • 连接到Windows服务控制管理器(SCM)。
    • 打开并查询DNS缓存服务(dnscache)的配置。
    • 如果服务被禁用,记录一条警告消息,提示用户重新启用该服务。
4. pitfallVirtioNetworkDriver

这个函数检查VirtIO网络驱动程序的版本是否过时,并在需要时提醒用户更新驱动程序。

  • 逻辑:
    • 通过调用Windows系统API查询已加载的内核模块列表。
    • 查找名为netkvm.sys的VirtIO网络驱动程序模块。
    • 获取驱动程序的版本信息,并与预定义的最小版本号进行比较。
    • 如果驱动程序版本过低,记录一条警告消息,建议用户更新驱动程序。
5. pitfallWeakHostSend

该函数检测网络接口的弱主机发送配置是否可能导致路由问题(如路由环路)。

  • 逻辑:
    • 获取当前系统的IP路由表。
    • 遍历配置文件中的每个对等端(Peer)的端点地址,查找与之对应的最佳路由条目。
    • 检查路由条目的接口是否启用了转发(Forwarding)或弱主机发送(WeakHostSend)功能,并判断是否与指定的LUID匹配。
    • 如果匹配且存在问题,记录警告信息,指出可能引发路由环路的接口。

总结与分析

  • 静态与动态检测:

    • evaluateStaticPitfalls 负责检测系统配置中的静态问题,例如DNS缓存服务的禁用和VirtIO网络驱动的版本问题。这些问题通常是在启动或配置阶段检测到的。
    • evaluateDynamicPitfalls 负责检测运行时配置中的动态问题,特别是路由相关的弱主机发送配置。此类问题可能导致通信错误或性能下降,特别是在涉及多网卡或复杂路由配置的场景中。
  • 错误处理与容错:

    • 代码中大量使用了错误处理来确保即使某些操作失败,整个程序也不会崩溃,例如查询服务配置或加载模块失败时,都会安全地退出并避免影响其他操作。
  • 系统集成:

    • 代码紧密集成了Windows API,以深入访问系统配置和状态,这对于特定环境(如虚拟化、复杂网络环境)下的网络问题诊断非常有效。
  • 日志与用户提示:

    • 每当检测到问题时,代码都会通过日志向用户发出警告,这对于用户的运维操作或问题排查具有重要指导意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值