وارد TUN
TUN (نفق الشبكة) هو آلية الوكيل الشفاف الرئيسية في sing-box. ينشئ واجهة شبكة افتراضية تلتقط جميع حركة مرور النظام. يستخدم sing-box مكتبة sing-tun التي تدعم تنفيذات مكدس شبكة متعددة، والتوجيه التلقائي، وإعادة التوجيه التلقائي عبر nftables.
المصدر: protocol/tun/inbound.go، sing-tun
البنية
type Inbound struct {
tag string
ctx context.Context
router adapter.Router
networkManager adapter.NetworkManager
logger log.ContextLogger
tunOptions tun.Options
udpTimeout time.Duration
stack string
tunIf tun.Tun
tunStack tun.Stack
platformInterface adapter.PlatformInterface
platformOptions option.TunPlatformOptions
autoRedirect tun.AutoRedirect
routeRuleSet []adapter.RuleSet
routeRuleSetCallback []*list.Element[adapter.RuleSetUpdateCallback]
routeExcludeRuleSet []adapter.RuleSet
routeExcludeRuleSetCallback []*list.Element[adapter.RuleSetUpdateCallback]
routeAddressSet []*netipx.IPSet
routeExcludeAddressSet []*netipx.IPSet
}اختيار MTU
يتم اختيار MTU تلقائياً بناءً على المنصة:
if tunMTU == 0 {
if platformInterface != nil && platformInterface.UnderNetworkExtension() {
// iOS/macOS Network Extension: 4064 (4096 - UTUN_IF_HEADROOM_SIZE)
tunMTU = 4064
} else if C.IsAndroid {
// Android: بعض الأجهزة تبلغ عن ENOBUFS مع 65535
tunMTU = 9000
} else {
tunMTU = 65535
}
}GSO (تفريغ التجزئة العام)
يتم تفعيل GSO تلقائياً على Linux عند استيفاء الشروط:
enableGSO := C.IsLinux && options.Stack == "gvisor" && platformInterface == nil && tunMTU > 0 && tunMTU < 49152خيارات مكدس الشبكة
يحدد خيار stack كيفية معالجة الحزم الملتقطة:
tunStack, _ := tun.NewStack(t.stack, tun.StackOptions{
Context: t.ctx,
Tun: tunInterface,
TunOptions: t.tunOptions,
UDPTimeout: t.udpTimeout,
Handler: t,
Logger: t.logger,
ForwarderBindInterface: forwarderBindInterface,
InterfaceFinder: t.networkManager.InterfaceFinder(),
IncludeAllNetworks: includeAllNetworks,
})المكدسات المتاحة
| المكدس | الوصف |
|---|---|
gvisor | مكدس TCP/IP لفضاء المستخدم من Google. أفضل توافق، أعلى استهلاك للمعالج. |
system | يستخدم مكدس نواة نظام التشغيل. استهلاك أقل للمعالج، يتطلب إعداداً على مستوى نظام التشغيل. |
mixed | gVisor لـ TCP، النظام لـ UDP. نهج متوازن. |
تكوين العناوين
يتم فصل عناوين IPv4 وIPv6 من قائمة Address الموحدة:
address := options.Address
inet4Address := common.Filter(address, func(it netip.Prefix) bool {
return it.Addr().Is4()
})
inet6Address := common.Filter(address, func(it netip.Prefix) bool {
return it.Addr().Is6()
})ينطبق نفس النمط على عناوين المسار وعناوين استثناء المسار.
خيارات TUN
هيكل خيارات TUN الكامل يتضمن:
tun.Options{
Name: options.InterfaceName,
MTU: tunMTU,
GSO: enableGSO,
Inet4Address: inet4Address,
Inet6Address: inet6Address,
AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
IncludeUID: includeUID,
ExcludeUID: excludeUID,
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
IncludeMACAddress: includeMACAddress,
ExcludeMACAddress: excludeMACAddress,
// ... فهارس جدول التوجيه، العلامات، إلخ
}تصفية UID
يمكن تحديد نطاقات UID كمعرفات فردية أو نطاقات:
includeUID := uidToRange(options.IncludeUID)
if len(options.IncludeUIDRange) > 0 {
includeUID, _ = parseRange(includeUID, options.IncludeUIDRange)
}يدعم تحليل النطاقات تنسيق start:end:
func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.Range[uint32], error) {
for _, uidRange := range rangeList {
subIndex := strings.Index(uidRange, ":")
start, _ := strconv.ParseUint(uidRange[:subIndex], 0, 32)
end, _ := strconv.ParseUint(uidRange[subIndex+1:], 0, 32)
uidRanges = append(uidRanges, ranges.New(uint32(start), uint32(end)))
}
}تصفية عنوان MAC
يتم تحليل عناوين MAC للتصفية على مستوى الشبكة المحلية:
for i, macString := range options.IncludeMACAddress {
mac, _ := net.ParseMAC(macString)
includeMACAddress = append(includeMACAddress, mac)
}التوجيه التلقائي (Auto-Route)
عند تفعيل auto_route، يقوم sing-box تلقائياً بتكوين جداول التوجيه لتوجيه حركة المرور عبر واجهة TUN. يتضمن التكوين:
IPRoute2TableIndex: tableIndex, // افتراضي: tun.DefaultIPRoute2TableIndex
IPRoute2RuleIndex: ruleIndex, // افتراضي: tun.DefaultIPRoute2RuleIndexإعادة التوجيه التلقائي (Auto-Redirect)
تستخدم إعادة التوجيه التلقائي nftables لإعادة توجيه حركة المرور بدون تعديل جدول التوجيه. تتطلب auto_route:
if options.AutoRedirect {
if !options.AutoRoute {
return nil, E.New("`auto_route` is required by `auto_redirect`")
}
inbound.autoRedirect, _ = tun.NewAutoRedirect(tun.AutoRedirectOptions{
TunOptions: &inbound.tunOptions,
Context: ctx,
Handler: (*autoRedirectHandler)(inbound),
Logger: logger,
NetworkMonitor: networkManager.NetworkMonitor(),
InterfaceFinder: networkManager.InterfaceFinder(),
TableName: "sing-box",
DisableNFTables: dErr == nil && disableNFTables,
RouteAddressSet: &inbound.routeAddressSet,
RouteExcludeAddressSet: &inbound.routeExcludeAddressSet,
})
}يمكن لمتغير البيئة DISABLE_NFTABLES فرض وضع iptables:
disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES"))علامات إعادة التوجيه التلقائي
تُستخدم علامات حركة المرور لمنع حلقات التوجيه:
AutoRedirectInputMark: inputMark, // افتراضي: tun.DefaultAutoRedirectInputMark
AutoRedirectOutputMark: outputMark, // افتراضي: tun.DefaultAutoRedirectOutputMark
AutoRedirectResetMark: resetMark, // افتراضي: tun.DefaultAutoRedirectResetMark
AutoRedirectNFQueue: nfQueue, // افتراضي: tun.DefaultAutoRedirectNFQueueمجموعات عناوين المسار
يدعم TUN مجموعات عناوين مسار ديناميكية من مجموعات القواعد:
for _, routeAddressSet := range options.RouteAddressSet {
ruleSet, loaded := router.RuleSet(routeAddressSet)
if !loaded {
return nil, E.New("rule-set not found: ", routeAddressSet)
}
inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet)
}عند تحديث مجموعات القواعد، يتم تحديث عناوين المسار:
func (t *Inbound) updateRouteAddressSet(it adapter.RuleSet) {
t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet)
t.autoRedirect.UpdateRouteAddressSet()
}البدء على مرحلتين
يستخدم TUN بدءاً على مرحلتين:
المرحلة 1: StartStateStart
- بناء قواعد Android إذا كان ذلك قابلاً للتطبيق
- حساب اسم الواجهة
- استخراج عناوين المسار من مجموعات القواعد
- فتح واجهة TUN (تعتمد على المنصة أو
tun.New()) - إنشاء مكدس الشبكة
المرحلة 2: StartStatePostStart
- بدء مكدس الشبكة
- بدء واجهة TUN
- تهيئة إعادة التوجيه التلقائي (إذا كانت مفعلة)
func (t *Inbound) Start(stage adapter.StartStage) error {
switch stage {
case adapter.StartStateStart:
// فتح TUN، إنشاء المكدس
if t.platformInterface != nil && t.platformInterface.UsePlatformInterface() {
tunInterface, _ = t.platformInterface.OpenInterface(&tunOptions, t.platformOptions)
} else {
tunInterface, _ = tun.New(tunOptions)
}
tunStack, _ := tun.NewStack(t.stack, stackOptions)
case adapter.StartStatePostStart:
t.tunStack.Start()
t.tunIf.Start()
if t.autoRedirect != nil {
t.autoRedirect.Start()
}
}
}معالجة الاتصال
PrepareConnection
قبل إنشاء الاتصالات، يفحص TUN قواعد التوجيه:
func (t *Inbound) PrepareConnection(network, source, destination, routeContext, timeout) (tun.DirectRouteDestination, error) {
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
Inbound: t.tag,
InboundType: C.TypeTun,
IPVersion: ipVersion,
Network: network,
Source: source,
Destination: destination,
}, routeContext, timeout, false)
// معالجة حالات التجاوز والرفض وICMP
}اتصالات TCP/UDP
التوجيه القياسي عبر الموجه:
func (t *Inbound) NewConnectionEx(ctx, conn, source, destination, onClose) {
metadata.Inbound = t.tag
metadata.InboundType = C.TypeTun
metadata.Source = source
metadata.Destination = destination
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}معالج إعادة التوجيه التلقائي
نوع معالج منفصل يعالج الاتصالات المعاد توجيهها تلقائياً:
type autoRedirectHandler Inbound
func (t *autoRedirectHandler) NewConnectionEx(ctx, conn, source, destination, onClose) {
// نفس النمط، لكن يُسجل كـ "redirect connection"
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}تكامل المنصة
على المنصات المحمولة (iOS/Android)، يستخدم TUN واجهة المنصة:
if t.platformInterface != nil && t.platformInterface.UsePlatformInterface() {
tunInterface, _ = t.platformInterface.OpenInterface(&tunOptions, t.platformOptions)
}تتضمن الخيارات الخاصة بالمنصة:
ForwarderBindInterface: ربط المحول بواجهة محددة (الهاتف المحمول)IncludeAllNetworks: خيار Network Extension لـ iOSMultiPendingPackets: حل بديل لـ Darwin مع MTU صغير
مثال على التكوين
{
"type": "tun",
"tag": "tun-in",
"interface_name": "tun0",
"address": ["172.19.0.1/30", "fdfe:dcba:9876::1/126"],
"mtu": 9000,
"auto_route": true,
"strict_route": true,
"stack": "mixed",
"route_address": ["0.0.0.0/0", "::/0"],
"route_exclude_address": ["192.168.0.0/16"],
"route_address_set": ["geoip-cn"],
"auto_redirect": true,
"include_package": ["com.example.app"],
"exclude_package": ["com.example.excluded"],
"udp_timeout": "5m"
}