package tunnel
import (
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
func findDefaultLUID(family winipcfg.AddressFamily, ourLUID winipcfg.LUID, lastLUID *winipcfg.LUID, lastIndex *uint32) error {
r, err := winipcfg.GetIPForwardTable2(family)
if err != nil {
return err
}
lowestMetric := ^uint32(0)
index := uint32(0)
luid := winipcfg.LUID(0)
for i := range r {
if r[i].DestinationPrefix.PrefixLength != 0 || r[i].InterfaceLUID == ourLUID {
continue
}
ifrow, err := r[i].InterfaceLUID.Interface()
if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp {
continue
}
iface, err := r[i].InterfaceLUID.IPInterface(family)
if err != nil {
continue
}
if r[i].Metric+iface.Metric < lowestMetric {
lowestMetric = r[i].Metric + iface.Metric
index = r[i].InterfaceIndex
luid = r[i].InterfaceLUID
}
}
if luid == *lastLUID && index == *lastIndex {
return nil
}
*lastLUID = luid
*lastIndex = index
return nil
}
func monitorMTU(family winipcfg.AddressFamily, ourLUID winipcfg.LUID) ([]winipcfg.ChangeCallback, error) {
var minMTU uint32
if family == windows.AF_INET {
minMTU = 576
} else if family == windows.AF_INET6 {
minMTU = 1280
}
lastLUID := winipcfg.LUID(0)
lastIndex := ^uint32(0)
lastMTU := uint32(0)
doIt := func() error {
err := findDefaultLUID(family, ourLUID, &lastLUID, &lastIndex)
if err != nil {
return err
}
mtu := uint32(0)
if lastLUID != 0 {
iface, err := lastLUID.Interface()
if err != nil {
return err
}
if iface.MTU > 0 {
mtu = iface.MTU
}
}
if mtu > 0 && lastMTU != mtu {
iface, err := ourLUID.IPInterface(family)
if err != nil {
return err
}
iface.NLMTU = mtu - 80
if iface.NLMTU < minMTU {
iface.NLMTU = minMTU
}
err = iface.Set()
if err != nil {
return err
}
lastMTU = mtu
}
return nil
}
err := doIt()
if err != nil {
return nil, err
}
cbr, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.MibIPforwardRow2) {
if route != nil && route.DestinationPrefix.PrefixLength == 0 {
doIt()
}
})
if err != nil {
return nil, err
}
cbi, err := winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
if notificationType == winipcfg.MibParameterNotification {
doIt()
}
})
if err != nil {
cbr.Unregister()
return nil, err
}
return []winipcfg.ChangeCallback{cbr, cbi}, nil
}
这段Go代码实现了监控和管理网络接口MTU(最大传输单元)的功能,尤其是用于WireGuard网络适配器在Windows系统中的操作。主要有两个核心函数:findDefaultLUID
和 monitorMTU
,它们共同协作以确保网络接口的MTU设置符合预期。
主要函数分析
1. findDefaultLUID
这个函数的目的是找到默认路由的接口LUID(Locally Unique Identifier)。它在指定的地址族(IPv4或IPv6)中查找当前系统的默认路由接口LUID和接口索引。
-
参数:
family
: 指定地址族(IPv4 或 IPv6)。ourLUID
: 当前接口的LUID,用于排除自身。lastLUID
: 上次记录的默认路由接口LUID。lastIndex
: 上次记录的默认路由接口索引。
-
逻辑:
- 调用
GetIPForwardTable2
获取路由表,根据前缀长度为0和接口操作状态为“Up”的条件筛选出默认路由。 - 遍历路由表,找出具有最低路由度量(Metric)的接口,记录其LUID和接口索引。
- 如果找到的新LUID和索引与上次记录的相同,函数直接返回。
- 否则,更新
lastLUID
和lastIndex
。
- 调用
-
应用场景:
- 用于动态查找系统默认的网络接口,这在需要基于路由表的动态配置时非常重要。
2. monitorMTU
这个函数负责监控并调整网络接口的MTU值,确保它符合默认路由的MTU设置,并注册路由变化和接口变化的回调函数,以便动态调整MTU。
-
参数:
family
: 地址族(IPv4 或 IPv6)。ourLUID
: 当前接口的LUID,用于排除自身。
-
逻辑:
- 根据地址族确定最小MTU值(IPv4的最小MTU是576字节,IPv6是1280字节)。
- 定义一个内部函数
doIt
,用于查找默认路由LUID并更新当前接口的MTU:- 调用
findDefaultLUID
获取默认路由的LUID和接口索引。 - 如果找到有效的MTU且与上次记录的MTU不同,则设置当前接口的MTU。
- 如果计算得出的MTU值低于最小MTU值,则设置为最小MTU。
- 调用
- 初次调用
doIt
以立即应用MTU设置。 - 注册两个回调函数:
RegisterRouteChangeCallback
:监控路由表变化,重新执行doIt
以更新MTU。RegisterInterfaceChangeCallback
:监控接口参数变化,重新执行doIt
以更新MTU。
- 返回注册的回调函数数组。
-
应用场景:
- 用于确保网络接口的MTU值动态跟随默认路由的MTU,特别是在路由或接口状态发生变化时。此功能在保持网络传输效率和避免分片传输中至关重要。
总结
-
动态配置与监控: 代码展示了如何通过实时监控和回调机制动态调整网络接口的配置,确保其MTU与系统默认路由相一致。这对于保持网络传输的稳定性和优化传输性能非常关键。
-
错误处理与回退: 代码中对错误处理也非常小心,在回调注册失败时,会确保先注销已成功注册的回调,避免资源泄漏或不一致的状态。
-
系统集成: 该代码紧密集成了Windows系统的网络接口配置API(如
winipcfg
包),展示了如何在Go语言中使用这些底层API来管理网络配置,尤其是在特定的VPN场景下,如WireGuard。