Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d97b0f06d6 | ||
|
|
5400abd7f7 | ||
|
|
e71911f94d | ||
|
|
3a6fedf64a | ||
|
|
42a84746a9 | ||
|
|
7f3af59109 | ||
|
|
8ccd51404e | ||
|
|
7bd004f141 | ||
|
|
d44e0c68d4 | ||
|
|
bc23daa800 | ||
|
|
e6c219a61e | ||
|
|
ddc824fb9c | ||
|
|
e229d7041e | ||
|
|
e2503223dc |
@@ -39,6 +39,10 @@ func closeAdapter(wintun *Adapter) {
|
||||
// deterministically. If it is set to nil, the GUID is chosen by the system at random,
|
||||
// and hence a new NLA entry is created for each new adapter.
|
||||
func CreateAdapter(name string, tunnelType string, requestedGUID *windows.GUID) (wintun *Adapter, err error) {
|
||||
err = procWintunCloseAdapter.Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var name16 *uint16
|
||||
name16, err = windows.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
|
||||
@@ -69,6 +69,7 @@ func (r *autoRedirect) Start() error {
|
||||
r.androidSu = true
|
||||
for _, suPath := range []string{
|
||||
"su",
|
||||
"/product/bin/su",
|
||||
"/system/bin/su",
|
||||
} {
|
||||
r.suPath, err = exec.LookPath(suPath)
|
||||
|
||||
@@ -182,6 +182,25 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
},
|
||||
},
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyIIFNAME,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: nftablesIfname(r.tunOptions.Name),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
|
||||
@@ -103,10 +103,6 @@ func (r *autoRedirect) nftablesCreateLocalAddressSets(
|
||||
update = true
|
||||
}
|
||||
}
|
||||
localAddresses6 = common.Filter(localAddresses6, func(it netip.Prefix) bool {
|
||||
address := it.Addr()
|
||||
return address.IsLoopback() || address.IsGlobalUnicast() && !address.IsPrivate()
|
||||
})
|
||||
if len(lastAddresses) == 0 || update {
|
||||
_, err := nftablesCreateIPSet(nft, table, 6, "inet6_local_address_set", nftables.TableFamilyIPv6, nil, localAddresses6, false, update)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type TCPNat struct {
|
||||
timeout time.Duration
|
||||
portIndex uint16
|
||||
portAccess sync.RWMutex
|
||||
addrAccess sync.RWMutex
|
||||
@@ -19,6 +20,7 @@ type TCPNat struct {
|
||||
}
|
||||
|
||||
type TCPSession struct {
|
||||
sync.Mutex
|
||||
Source netip.AddrPort
|
||||
Destination netip.AddrPort
|
||||
LastActive time.Time
|
||||
@@ -26,38 +28,41 @@ type TCPSession struct {
|
||||
|
||||
func NewNat(ctx context.Context, timeout time.Duration) *TCPNat {
|
||||
natMap := &TCPNat{
|
||||
timeout: timeout,
|
||||
portIndex: 10000,
|
||||
addrMap: make(map[netip.AddrPort]uint16),
|
||||
portMap: make(map[uint16]*TCPSession),
|
||||
}
|
||||
go natMap.loopCheckTimeout(ctx, timeout)
|
||||
go natMap.loopCheckTimeout(ctx)
|
||||
return natMap
|
||||
}
|
||||
|
||||
func (n *TCPNat) loopCheckTimeout(ctx context.Context, timeout time.Duration) {
|
||||
ticker := time.NewTicker(timeout)
|
||||
func (n *TCPNat) loopCheckTimeout(ctx context.Context) {
|
||||
ticker := time.NewTicker(n.timeout)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
n.checkTimeout(timeout)
|
||||
n.checkTimeout()
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *TCPNat) checkTimeout(timeout time.Duration) {
|
||||
func (n *TCPNat) checkTimeout() {
|
||||
now := time.Now()
|
||||
n.portAccess.Lock()
|
||||
defer n.portAccess.Unlock()
|
||||
n.addrAccess.Lock()
|
||||
defer n.addrAccess.Unlock()
|
||||
for natPort, session := range n.portMap {
|
||||
if now.Sub(session.LastActive) > timeout {
|
||||
session.Lock()
|
||||
if now.Sub(session.LastActive) > n.timeout {
|
||||
delete(n.addrMap, session.Source)
|
||||
delete(n.portMap, natPort)
|
||||
}
|
||||
session.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +71,11 @@ func (n *TCPNat) LookupBack(port uint16) *TCPSession {
|
||||
session := n.portMap[port]
|
||||
n.portAccess.RUnlock()
|
||||
if session != nil {
|
||||
session.LastActive = time.Now()
|
||||
session.Lock()
|
||||
if time.Since(session.LastActive) > time.Second {
|
||||
session.LastActive = time.Now()
|
||||
}
|
||||
session.Unlock()
|
||||
}
|
||||
return session
|
||||
}
|
||||
|
||||
72
tun.go
72
tun.go
@@ -52,44 +52,46 @@ type DarwinTUN interface {
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultIPRoute2TableIndex = 2022
|
||||
DefaultIPRoute2RuleIndex = 9000
|
||||
DefaultIPRoute2TableIndex = 2022
|
||||
DefaultIPRoute2RuleIndex = 9000
|
||||
DefaultIPRoute2AutoRedirectFallbackRuleIndex = 32768
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Name string
|
||||
Inet4Address []netip.Prefix
|
||||
Inet6Address []netip.Prefix
|
||||
MTU uint32
|
||||
GSO bool
|
||||
AutoRoute bool
|
||||
InterfaceScope bool
|
||||
Inet4Gateway netip.Addr
|
||||
Inet6Gateway netip.Addr
|
||||
DNSServers []netip.Addr
|
||||
IPRoute2TableIndex int
|
||||
IPRoute2RuleIndex int
|
||||
AutoRedirectMarkMode bool
|
||||
AutoRedirectInputMark uint32
|
||||
AutoRedirectOutputMark uint32
|
||||
Inet4LoopbackAddress []netip.Addr
|
||||
Inet6LoopbackAddress []netip.Addr
|
||||
StrictRoute bool
|
||||
Inet4RouteAddress []netip.Prefix
|
||||
Inet6RouteAddress []netip.Prefix
|
||||
Inet4RouteExcludeAddress []netip.Prefix
|
||||
Inet6RouteExcludeAddress []netip.Prefix
|
||||
IncludeInterface []string
|
||||
ExcludeInterface []string
|
||||
IncludeUID []ranges.Range[uint32]
|
||||
ExcludeUID []ranges.Range[uint32]
|
||||
IncludeAndroidUser []int
|
||||
IncludePackage []string
|
||||
ExcludePackage []string
|
||||
InterfaceFinder control.InterfaceFinder
|
||||
InterfaceMonitor DefaultInterfaceMonitor
|
||||
FileDescriptor int
|
||||
Logger logger.Logger
|
||||
Name string
|
||||
Inet4Address []netip.Prefix
|
||||
Inet6Address []netip.Prefix
|
||||
MTU uint32
|
||||
GSO bool
|
||||
AutoRoute bool
|
||||
InterfaceScope bool
|
||||
Inet4Gateway netip.Addr
|
||||
Inet6Gateway netip.Addr
|
||||
DNSServers []netip.Addr
|
||||
IPRoute2TableIndex int
|
||||
IPRoute2RuleIndex int
|
||||
IPRoute2AutoRedirectFallbackRuleIndex int
|
||||
AutoRedirectMarkMode bool
|
||||
AutoRedirectInputMark uint32
|
||||
AutoRedirectOutputMark uint32
|
||||
Inet4LoopbackAddress []netip.Addr
|
||||
Inet6LoopbackAddress []netip.Addr
|
||||
StrictRoute bool
|
||||
Inet4RouteAddress []netip.Prefix
|
||||
Inet6RouteAddress []netip.Prefix
|
||||
Inet4RouteExcludeAddress []netip.Prefix
|
||||
Inet6RouteExcludeAddress []netip.Prefix
|
||||
IncludeInterface []string
|
||||
ExcludeInterface []string
|
||||
IncludeUID []ranges.Range[uint32]
|
||||
ExcludeUID []ranges.Range[uint32]
|
||||
IncludeAndroidUser []int
|
||||
IncludePackage []string
|
||||
ExcludePackage []string
|
||||
InterfaceFinder control.InterfaceFinder
|
||||
InterfaceMonitor DefaultInterfaceMonitor
|
||||
FileDescriptor int
|
||||
Logger logger.Logger
|
||||
|
||||
// No work for TCP, do not use.
|
||||
_TXChecksumOffload bool
|
||||
|
||||
@@ -152,7 +152,10 @@ func (t *NativeTun) Start() error {
|
||||
|
||||
func (t *NativeTun) Close() error {
|
||||
defer flushDNSCache()
|
||||
return E.Errors(t.unsetRoutes(), t.tunFile.Close())
|
||||
t.stopFd.Stop()
|
||||
err := E.Errors(t.unsetRoutes(), t.tunFile.Close())
|
||||
t.stopFd.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *NativeTun) Read(p []byte) (n int, err error) {
|
||||
@@ -347,6 +350,9 @@ func (t *NativeTun) BatchRead() ([]*buf.Buffer, error) {
|
||||
t.buffers = t.buffers[:0]
|
||||
return nil, errno
|
||||
}
|
||||
if n < 0 {
|
||||
return nil, os.ErrClosed
|
||||
}
|
||||
if n < 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
41
tun_linux.go
41
tun_linux.go
@@ -130,7 +130,7 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
|
||||
for _, address := range t.options.Inet4Address {
|
||||
addr4, _ := netlink.ParseAddr(address.String())
|
||||
err = netlink.AddrAdd(tunLink, addr4)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
|
||||
for _, address := range t.options.Inet6Address {
|
||||
addr6, _ := netlink.ParseAddr(address.String())
|
||||
err = netlink.AddrAdd(tunLink, addr6)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -315,6 +315,7 @@ func (t *NativeTun) Close() error {
|
||||
if t.interfaceCallback != nil {
|
||||
t.options.InterfaceMonitor.UnregisterCallback(t.interfaceCallback)
|
||||
}
|
||||
t.unsetAddresses()
|
||||
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
|
||||
}
|
||||
|
||||
@@ -616,6 +617,22 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
}
|
||||
// Fallback rules after system default rules (32766: main, 32767: default)
|
||||
// Only reached when main and default tables have no route
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = t.options.IPRoute2AutoRedirectFallbackRuleIndex
|
||||
it.Table = t.options.IPRoute2TableIndex
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = t.options.IPRoute2AutoRedirectFallbackRuleIndex
|
||||
it.Table = t.options.IPRoute2TableIndex
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
@@ -989,7 +1006,7 @@ func (t *NativeTun) unsetRules() error {
|
||||
for _, rule := range ruleList {
|
||||
ruleStart := t.options.IPRoute2RuleIndex
|
||||
ruleEnd := ruleStart + 10
|
||||
if rule.Priority >= ruleStart && rule.Priority <= ruleEnd {
|
||||
if rule.Priority >= ruleStart && rule.Priority <= ruleEnd || (t.options.AutoRedirectMarkMode && rule.Priority == t.options.IPRoute2AutoRedirectFallbackRuleIndex) {
|
||||
ruleToDel := netlink.NewRule()
|
||||
ruleToDel.Family = rule.Family
|
||||
ruleToDel.Priority = rule.Priority
|
||||
@@ -1003,6 +1020,24 @@ func (t *NativeTun) unsetRules() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NativeTun) unsetAddresses() {
|
||||
if t.options.FileDescriptor > 0 {
|
||||
return
|
||||
}
|
||||
tunLink, err := netlink.LinkByName(t.options.Name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, address := range t.options.Inet4Address {
|
||||
addr, _ := netlink.ParseAddr(address.String())
|
||||
_ = netlink.AddrDel(tunLink, addr)
|
||||
}
|
||||
for _, address := range t.options.Inet6Address {
|
||||
addr, _ := netlink.ParseAddr(address.String())
|
||||
_ = netlink.AddrDel(tunLink, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *NativeTun) resetRules() error {
|
||||
t.unsetRules()
|
||||
return t.setRules()
|
||||
|
||||
@@ -181,6 +181,13 @@ func (t *NativeTun) Start() error {
|
||||
return err
|
||||
}
|
||||
if t.options.StrictRoute {
|
||||
major, _, _ := windows.RtlGetNtVersionNumbers()
|
||||
if major < 10 {
|
||||
if t.options.Logger != nil {
|
||||
t.options.Logger.Warn("strict routing is not supported on Windows versions below 10")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var engine uintptr
|
||||
session := &winsys.FWPM_SESSION0{Flags: winsys.FWPM_SESSION_FLAG_DYNAMIC}
|
||||
err := winsys.FwpmEngineOpen0(nil, winsys.RPC_C_AUTHN_DEFAULT, nil, session, unsafe.Pointer(&engine))
|
||||
@@ -395,15 +402,16 @@ retry:
|
||||
|
||||
func (t *NativeTun) ReadPacket() ([]byte, func(), error) {
|
||||
t.running.Add(1)
|
||||
defer t.running.Done()
|
||||
retry:
|
||||
if t.close.Load() == 1 {
|
||||
t.running.Done()
|
||||
return nil, nil, os.ErrClosed
|
||||
}
|
||||
start := nanotime()
|
||||
shouldSpin := t.rate.current.Load() >= spinloopRateThreshold && uint64(start-t.rate.nextStartTime.Load()) <= rateMeasurementGranularity*2
|
||||
for {
|
||||
if t.close.Load() == 1 {
|
||||
t.running.Done()
|
||||
return nil, nil, os.ErrClosed
|
||||
}
|
||||
packet, err := t.session.ReceivePacket()
|
||||
@@ -411,7 +419,10 @@ retry:
|
||||
case nil:
|
||||
packetSize := len(packet)
|
||||
t.rate.update(uint64(packetSize))
|
||||
return packet, func() { t.session.ReleaseReceivePacket(packet) }, nil
|
||||
return packet, func() {
|
||||
t.session.ReleaseReceivePacket(packet)
|
||||
t.running.Done()
|
||||
}, nil
|
||||
case windows.ERROR_NO_MORE_ITEMS:
|
||||
if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration {
|
||||
windows.WaitForSingleObject(t.readWait, windows.INFINITE)
|
||||
@@ -420,10 +431,13 @@ retry:
|
||||
procyield(1)
|
||||
continue
|
||||
case windows.ERROR_HANDLE_EOF:
|
||||
t.running.Done()
|
||||
return nil, nil, os.ErrClosed
|
||||
case windows.ERROR_INVALID_DATA:
|
||||
t.running.Done()
|
||||
return nil, nil, errors.New("send ring corrupt")
|
||||
}
|
||||
t.running.Done()
|
||||
return nil, nil, fmt.Errorf("read failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user