mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-11-10 21:21:02 +08:00
Rework forward NAT with UDP mapping and fragment support
This commit is contained in:
24 files changed
+507
-116
No files matched your search
@@ -0,0 +1,47 @@
|
|||||||
|
package kernelports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"slices"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrExhausted = E.New("kernel ports: no more ports can be reserved")
|
||||||
|
|
||||||
|
func (p *Pool) ranges(protocol uint8) *atomic.Pointer[[]tun.SelectorRange] {
|
||||||
|
if protocol == uint8(header.UDPProtocolNumber) {
|
||||||
|
return &p.udp
|
||||||
|
}
|
||||||
|
return &p.tcp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) PortSelectorRanges(protocol uint8) []tun.SelectorRange {
|
||||||
|
ranges := p.ranges(protocol).Load()
|
||||||
|
if ranges == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return *ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) ExpandSelectorRanges(protocol uint8) bool {
|
||||||
|
_, err := p.Acquire(protocol)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) Contains(protocol uint8, port uint16) bool {
|
||||||
|
return slices.ContainsFunc(p.PortSelectorRanges(protocol), func(selectorRange tun.SelectorRange) bool {
|
||||||
|
return port >= selectorRange.Start && port-selectorRange.Start < selectorRange.Count
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) ReleaseSelector(protocol uint8, address netip.AddrPort) {
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ tun.PortWithSelectorRange = (*Pool)(nil)
|
||||||
|
_ tun.PortWithSelectorReservation = (*Pool)(nil)
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package kernelports
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultEphemeralFirst = 49152
|
||||||
|
defaultEphemeralLast = 65535
|
||||||
|
)
|
||||||
|
|
||||||
|
func ephemeralRange() (uint16, uint16) {
|
||||||
|
first, firstErr := unix.SysctlUint32("net.inet.ip.portrange.first")
|
||||||
|
last, lastErr := unix.SysctlUint32("net.inet.ip.portrange.last")
|
||||||
|
if firstErr != nil || lastErr != nil || first > last || last > 65535 {
|
||||||
|
return defaultEphemeralFirst, defaultEphemeralLast
|
||||||
|
}
|
||||||
|
return uint16(first), uint16(last)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package kernelports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultEphemeralFirst = 32768
|
||||||
|
defaultEphemeralLast = 60999
|
||||||
|
)
|
||||||
|
|
||||||
|
func ephemeralRange() (uint16, uint16) {
|
||||||
|
content, err := os.ReadFile("/proc/sys/net/ipv4/ip_local_port_range")
|
||||||
|
if err != nil {
|
||||||
|
return defaultEphemeralFirst, defaultEphemeralLast
|
||||||
|
}
|
||||||
|
fields := strings.Fields(string(content))
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return defaultEphemeralFirst, defaultEphemeralLast
|
||||||
|
}
|
||||||
|
first, firstErr := strconv.ParseUint(fields[0], 10, 16)
|
||||||
|
last, lastErr := strconv.ParseUint(fields[1], 10, 16)
|
||||||
|
if firstErr != nil || lastErr != nil || first > last {
|
||||||
|
return defaultEphemeralFirst, defaultEphemeralLast
|
||||||
|
}
|
||||||
|
return uint16(first), uint16(last)
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package kernelports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/netip"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const lowestSelector = 1024
|
||||||
|
|
||||||
|
type Pool struct {
|
||||||
|
tcp atomic.Pointer[[]tun.SelectorRange]
|
||||||
|
udp atomic.Pointer[[]tun.SelectorRange]
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() (*Pool, error) {
|
||||||
|
first, last := ephemeralRange()
|
||||||
|
below := tun.SelectorRange{Start: lowestSelector}
|
||||||
|
if first > lowestSelector {
|
||||||
|
below.Count = first - lowestSelector
|
||||||
|
}
|
||||||
|
above := tun.SelectorRange{Start: last + 1, Count: 65535 - last}
|
||||||
|
selected := below
|
||||||
|
if above.Count > below.Count {
|
||||||
|
selected = above
|
||||||
|
}
|
||||||
|
if selected.Count == 0 {
|
||||||
|
selected = tun.SelectorRange{Start: 49152, Count: 65535 - 49152 + 1}
|
||||||
|
}
|
||||||
|
ranges := []tun.SelectorRange{selected}
|
||||||
|
pool := &Pool{}
|
||||||
|
pool.tcp.Store(&ranges)
|
||||||
|
pool.udp.Store(&ranges)
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) Acquire(protocol uint8) (tun.SelectorRange, error) {
|
||||||
|
return tun.SelectorRange{}, ErrExhausted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) ReserveSelector(protocol uint8, address netip.AddrPort) bool {
|
||||||
|
var socketType int
|
||||||
|
switch protocol {
|
||||||
|
case uint8(header.TCPProtocolNumber):
|
||||||
|
socketType = unix.SOCK_STREAM
|
||||||
|
case uint8(header.UDPProtocolNumber):
|
||||||
|
socketType = unix.SOCK_DGRAM
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
family := unix.AF_INET
|
||||||
|
var sockaddr unix.Sockaddr
|
||||||
|
if address.Addr().Is4() {
|
||||||
|
sockaddr4 := &unix.SockaddrInet4{Port: int(address.Port())}
|
||||||
|
sockaddr4.Addr = address.Addr().As4()
|
||||||
|
sockaddr = sockaddr4
|
||||||
|
} else {
|
||||||
|
family = unix.AF_INET6
|
||||||
|
sockaddr6 := &unix.SockaddrInet6{Port: int(address.Port())}
|
||||||
|
sockaddr6.Addr = address.Addr().As16()
|
||||||
|
sockaddr = sockaddr6
|
||||||
|
}
|
||||||
|
syscall.ForkLock.RLock()
|
||||||
|
fd, err := unix.Socket(family, socketType, 0)
|
||||||
|
if err == nil {
|
||||||
|
unix.CloseOnExec(fd)
|
||||||
|
}
|
||||||
|
syscall.ForkLock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
defer unix.Close(fd)
|
||||||
|
err = unix.Bind(fd, sockaddr)
|
||||||
|
return !errors.Is(err, unix.EADDRINUSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build !windows && !linux && !darwin
|
||||||
|
|
||||||
|
package kernelports
|
||||||
|
|
||||||
|
func ephemeralRange() (uint16, uint16) {
|
||||||
|
return 49152, 65535
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package kernelports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net/netip"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
blockSize uint16 = 1024
|
||||||
|
maxBlocks int = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// SIO_ACQUIRE_PORT_RESERVATION = _WSAIOW(IOC_VENDOR, 100):
|
||||||
|
// IOC_IN | IOC_VENDOR | 100. Despite the write-only direction code, the
|
||||||
|
// reservation result is written to the WSAIoctl output buffer; using
|
||||||
|
// _WSAIORW instead is rejected with WSAEOPNOTSUPP.
|
||||||
|
const sioAcquirePortReservation uint32 = 0x80000000 | 0x18000000 | 100
|
||||||
|
|
||||||
|
type Pool struct {
|
||||||
|
tcp atomic.Pointer[[]tun.SelectorRange]
|
||||||
|
udp atomic.Pointer[[]tun.SelectorRange]
|
||||||
|
|
||||||
|
access sync.Mutex
|
||||||
|
blocks map[uint8][]windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() (*Pool, error) {
|
||||||
|
pool := &Pool{blocks: make(map[uint8][]windows.Handle)}
|
||||||
|
for _, protocol := range []uint8{uint8(header.TCPProtocolNumber), uint8(header.UDPProtocolNumber)} {
|
||||||
|
_, err := pool.Acquire(protocol)
|
||||||
|
if err != nil {
|
||||||
|
pool.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A runtime reservation only binds the protocol of the socket it was
|
||||||
|
// acquired on: both ephemeral auto-assignment and explicit binds of that
|
||||||
|
// protocol skip the block, while sockets of the other protocol still receive
|
||||||
|
// ports inside it (observed on Windows 11; the documentation does not say).
|
||||||
|
// Repeated wildcard requests from one process return distinct,
|
||||||
|
// non-contiguous blocks. Closing the socket releases the reservation.
|
||||||
|
func (p *Pool) Acquire(protocol uint8) (tun.SelectorRange, error) {
|
||||||
|
socketType, socketProtocol := windows.SOCK_STREAM, windows.IPPROTO_TCP
|
||||||
|
if protocol == uint8(header.UDPProtocolNumber) {
|
||||||
|
socketType, socketProtocol = windows.SOCK_DGRAM, windows.IPPROTO_UDP
|
||||||
|
} else {
|
||||||
|
protocol = uint8(header.TCPProtocolNumber)
|
||||||
|
}
|
||||||
|
p.access.Lock()
|
||||||
|
defer p.access.Unlock()
|
||||||
|
if len(p.blocks[protocol]) >= maxBlocks {
|
||||||
|
return tun.SelectorRange{}, ErrExhausted
|
||||||
|
}
|
||||||
|
socket, err := windows.Socket(windows.AF_INET, socketType, socketProtocol)
|
||||||
|
if err != nil {
|
||||||
|
return tun.SelectorRange{}, E.Cause(err, "create reservation socket")
|
||||||
|
}
|
||||||
|
// INET_PORT_RANGE { USHORT StartPort; USHORT NumberOfPorts; }.
|
||||||
|
// StartPort 0 requests a runtime (wildcard) reservation.
|
||||||
|
var in [4]byte
|
||||||
|
binary.LittleEndian.PutUint16(in[0:2], 0)
|
||||||
|
binary.LittleEndian.PutUint16(in[2:4], blockSize)
|
||||||
|
// INET_PORT_RESERVATION_INSTANCE {
|
||||||
|
// INET_PORT_RESERVATION { USHORT StartPort; USHORT NumberOfPorts; };
|
||||||
|
// INET_PORT_RESERVATION_TOKEN { ULONG64 Token; };
|
||||||
|
// } — the ULONG64 forces 8-byte alignment, so Token sits at offset 8.
|
||||||
|
var out [16]byte
|
||||||
|
var returned uint32
|
||||||
|
err = windows.WSAIoctl(socket, sioAcquirePortReservation,
|
||||||
|
&in[0], uint32(len(in)), &out[0], uint32(len(out)), &returned, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
windows.Closesocket(socket)
|
||||||
|
return tun.SelectorRange{}, E.Cause(err, "acquire port reservation")
|
||||||
|
}
|
||||||
|
// StartPort is returned in network byte order (as documented for
|
||||||
|
// INET_PORT_RANGE); NumberOfPorts is a plain host-order count.
|
||||||
|
startPort := binary.BigEndian.Uint16(out[0:2])
|
||||||
|
reservedCount := binary.LittleEndian.Uint16(out[2:4])
|
||||||
|
if startPort == 0 || reservedCount < blockSize {
|
||||||
|
windows.Closesocket(socket)
|
||||||
|
return tun.SelectorRange{}, E.New("acquire port reservation: stack returned ", reservedCount, " of ", blockSize, " ports")
|
||||||
|
}
|
||||||
|
p.blocks[protocol] = append(p.blocks[protocol], socket)
|
||||||
|
block := tun.SelectorRange{Start: startPort, Count: blockSize}
|
||||||
|
ranges := p.ranges(protocol)
|
||||||
|
var updated []tun.SelectorRange
|
||||||
|
current := ranges.Load()
|
||||||
|
if current != nil {
|
||||||
|
updated = append(updated, *current...)
|
||||||
|
}
|
||||||
|
updated = append(updated, block)
|
||||||
|
ranges.Store(&updated)
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) ReserveSelector(protocol uint8, address netip.AddrPort) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) Close() error {
|
||||||
|
p.access.Lock()
|
||||||
|
defer p.access.Unlock()
|
||||||
|
for _, sockets := range p.blocks {
|
||||||
|
for _, socket := range sockets {
|
||||||
|
windows.Closesocket(socket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear(p.blocks)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -14,8 +14,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/kernelports"
|
||||||
"github.com/sagernet/sing-box/common/windivert"
|
"github.com/sagernet/sing-box/common/windivert"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing-tun/gtcpip"
|
"github.com/sagernet/sing-tun/gtcpip"
|
||||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
@@ -26,8 +28,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bridgeReservedPortCount uint16 = 1024
|
|
||||||
|
|
||||||
bridgeICMPFlowTimeout = time.Minute
|
bridgeICMPFlowTimeout = time.Minute
|
||||||
|
|
||||||
bridgeDivertPriority int16 = 0
|
bridgeDivertPriority int16 = 0
|
||||||
@@ -123,8 +123,9 @@ type backendWindows struct {
|
|||||||
|
|
||||||
egress atomic.Pointer[egressState]
|
egress atomic.Pointer[egressState]
|
||||||
|
|
||||||
reservation *portReservation
|
ports *kernelports.Pool
|
||||||
reservedStart uint16
|
tcpRanges atomic.Pointer[[]tun.SelectorRange]
|
||||||
|
udpRanges atomic.Pointer[[]tun.SelectorRange]
|
||||||
|
|
||||||
icmp4, icmp6 *icmpTable
|
icmp4, icmp6 *icmpTable
|
||||||
|
|
||||||
@@ -153,14 +154,19 @@ func (b *backendWindows) start(scope *adapter.Scope) error {
|
|||||||
state := b.currentEgressState()
|
state := b.currentEgressState()
|
||||||
b.egress.Store(state)
|
b.egress.Store(state)
|
||||||
|
|
||||||
err := b.acquireReservations()
|
ports, err := kernelports.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return E.Cause(err, "bridge: reserve ports")
|
||||||
}
|
}
|
||||||
scope.Add(func() error {
|
b.ports = ports
|
||||||
b.reservation.Close()
|
scope.Add(ports.Close)
|
||||||
return nil
|
for _, protocol := range []uint8{uint8(header.TCPProtocolNumber), uint8(header.UDPProtocolNumber)} {
|
||||||
})
|
for _, block := range ports.PortSelectorRanges(protocol) {
|
||||||
|
b.publishRange(protocol, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.icmp4 = newICMPTable(bridgeICMPFlowTimeout)
|
||||||
|
b.icmp6 = newICMPTable(bridgeICMPFlowTimeout)
|
||||||
|
|
||||||
injectHandle, err := windivert.Open(nil, windivert.LayerNetwork, windivert.PriorityHighest, windivert.FlagSendOnly)
|
injectHandle, err := windivert.Open(nil, windivert.LayerNetwork, windivert.PriorityHighest, windivert.FlagSendOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -201,20 +207,62 @@ func (b *backendWindows) egressLabel() string {
|
|||||||
return "auto"
|
return "auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendWindows) acquireReservations() error {
|
func (b *backendWindows) selectorRanges(protocol uint8) *atomic.Pointer[[]tun.SelectorRange] {
|
||||||
reservation, err := acquirePortReservation(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP, bridgeReservedPortCount)
|
if protocol == uint8(header.UDPProtocolNumber) {
|
||||||
if err != nil {
|
return &b.udpRanges
|
||||||
return E.Cause(err, "bridge: reserve ports")
|
|
||||||
}
|
}
|
||||||
b.reservation = reservation
|
return &b.tcpRanges
|
||||||
b.reservedStart = reservation.startPort
|
|
||||||
b.icmp4 = newICMPTable(bridgeICMPFlowTimeout)
|
|
||||||
b.icmp6 = newICMPTable(bridgeICMPFlowTimeout)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendWindows) PortSelectorRange() (uint16, uint16) {
|
func (b *backendWindows) PortSelectorRanges(protocol uint8) []tun.SelectorRange {
|
||||||
return b.reservedStart, bridgeReservedPortCount
|
ranges := b.selectorRanges(protocol).Load()
|
||||||
|
if ranges == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return *ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendWindows) publishRange(protocol uint8, block tun.SelectorRange) {
|
||||||
|
ranges := b.selectorRanges(protocol)
|
||||||
|
var updated []tun.SelectorRange
|
||||||
|
current := ranges.Load()
|
||||||
|
if current != nil {
|
||||||
|
updated = append(updated, *current...)
|
||||||
|
}
|
||||||
|
updated = append(updated, block)
|
||||||
|
ranges.Store(&updated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backendWindows) ExpandSelectorRanges(protocol uint8) bool {
|
||||||
|
b.egressAccess.Lock()
|
||||||
|
defer b.egressAccess.Unlock()
|
||||||
|
block, err := b.ports.Acquire(protocol)
|
||||||
|
if err != nil {
|
||||||
|
b.logger.Debug(E.Cause(err, "bridge: expand selector range"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
state := b.egress.Load()
|
||||||
|
for _, isV6 := range []bool{false, true} {
|
||||||
|
addresses := state.divertAddresses(isV6)
|
||||||
|
if len(addresses) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = b.openTransportDiverter(addresses, isV6, protocol, block)
|
||||||
|
if err != nil {
|
||||||
|
b.logger.Warn(E.Cause(err, "bridge: divert expanded selector range"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.publishRange(protocol, block)
|
||||||
|
b.logger.Info("bridge: reserved ", block.Count, " more ", transportName(protocol), " ports")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func transportName(protocol uint8) string {
|
||||||
|
if protocol == uint8(header.UDPProtocolNumber) {
|
||||||
|
return "UDP"
|
||||||
|
}
|
||||||
|
return "TCP"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendWindows) rebuildDivertersLocked(state *egressState) error {
|
func (b *backendWindows) rebuildDivertersLocked(state *egressState) error {
|
||||||
@@ -245,19 +293,37 @@ func (b *backendWindows) closeDivertersLocked() {
|
|||||||
b.diverters = nil
|
b.diverters = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *backendWindows) openTransportDiverter(addresses []netip.Addr, isV6 bool, protocol uint8, block tun.SelectorRange) error {
|
||||||
|
portHigh := uint16(uint32(block.Start) + uint32(block.Count) - 1)
|
||||||
|
var (
|
||||||
|
filter *windivert.Filter
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if protocol == uint8(header.UDPProtocolNumber) {
|
||||||
|
filter, err = windivert.InboundUDPPortRange(addresses, block.Start, portHigh)
|
||||||
|
} else {
|
||||||
|
filter, err = windivert.InboundTCPPortRange(addresses, block.Start, portHigh)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "bridge: build ", transportName(protocol), " divert filter")
|
||||||
|
}
|
||||||
|
return b.openDiverter(filter, divertTransport, isV6)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *backendWindows) openFamilyDiverters(addresses []netip.Addr, isV6 bool) error {
|
func (b *backendWindows) openFamilyDiverters(addresses []netip.Addr, isV6 bool) error {
|
||||||
portHigh := uint16(uint32(b.reservedStart) + uint32(bridgeReservedPortCount) - 1)
|
for _, protocol := range []uint8{uint8(header.TCPProtocolNumber), uint8(header.UDPProtocolNumber)} {
|
||||||
|
for _, block := range b.PortSelectorRanges(protocol) {
|
||||||
|
err := b.openTransportDiverter(addresses, isV6, protocol, block)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
entries := []struct {
|
entries := []struct {
|
||||||
what string
|
what string
|
||||||
kind divertKind
|
kind divertKind
|
||||||
build func() (*windivert.Filter, error)
|
build func() (*windivert.Filter, error)
|
||||||
}{
|
}{
|
||||||
{"TCP", divertTransport, func() (*windivert.Filter, error) {
|
|
||||||
return windivert.InboundTCPPortRange(addresses, b.reservedStart, portHigh)
|
|
||||||
}},
|
|
||||||
{"UDP", divertTransport, func() (*windivert.Filter, error) {
|
|
||||||
return windivert.InboundUDPPortRange(addresses, b.reservedStart, portHigh)
|
|
||||||
}},
|
|
||||||
{"ICMP echo", divertICMPEcho, func() (*windivert.Filter, error) {
|
{"ICMP echo", divertICMPEcho, func() (*windivert.Filter, error) {
|
||||||
return windivert.InboundICMPEchoReply(addresses)
|
return windivert.InboundICMPEchoReply(addresses)
|
||||||
}},
|
}},
|
||||||
@@ -474,7 +540,7 @@ func (b *backendWindows) embeddedFlowActive(protocol tcpip.TransportProtocolNumb
|
|||||||
if len(transport) < 4 {
|
if len(transport) < 4 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return b.portReserved(binary.BigEndian.Uint16(transport[0:2]))
|
return b.selectorReserved(uint8(protocol), binary.BigEndian.Uint16(transport[0:2]))
|
||||||
case header.ICMPv4ProtocolNumber:
|
case header.ICMPv4ProtocolNumber:
|
||||||
if isV6 || len(transport) < header.ICMPv4MinimumSize {
|
if isV6 || len(transport) < header.ICMPv4MinimumSize {
|
||||||
return false
|
return false
|
||||||
@@ -500,8 +566,10 @@ func (b *backendWindows) embeddedFlowActive(protocol tcpip.TransportProtocolNumb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendWindows) portReserved(port uint16) bool {
|
func (b *backendWindows) selectorReserved(protocol uint8, port uint16) bool {
|
||||||
return port >= b.reservedStart && uint32(port) < uint32(b.reservedStart)+uint32(bridgeReservedPortCount)
|
return slices.ContainsFunc(b.PortSelectorRanges(protocol), func(block tun.SelectorRange) bool {
|
||||||
|
return port >= block.Start && port-block.Start < block.Count
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *backendWindows) deliver(packets [][]byte) {
|
func (b *backendWindows) deliver(packets [][]byte) {
|
||||||
@@ -659,7 +727,7 @@ func (b *backendWindows) prepareOutbound(packet []byte, state *egressState) bool
|
|||||||
if len(info.transport) < 4 {
|
if len(info.transport) < 4 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !b.portReserved(binary.BigEndian.Uint16(info.transport[0:2])) {
|
if !b.selectorReserved(uint8(info.protocol), binary.BigEndian.Uint16(info.transport[0:2])) {
|
||||||
b.logger.Debug("bridge: dropping outbound packet with source port outside the reserved block")
|
b.logger.Debug("bridge: dropping outbound packet with source port outside the reserved block")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,11 +106,8 @@ func (o *Outbound) PortMTU() uint32 {
|
|||||||
return o.backend.PortMTU()
|
return o.backend.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Outbound) PortSelectorRange() (uint16, uint16) {
|
func (o *Outbound) UpstreamPort() any {
|
||||||
if rangedBackend, isRanged := o.backend.(tun.PortWithSelectorRange); isRanged {
|
return o.backend
|
||||||
return rangedBackend.PortSelectorRange()
|
|
||||||
}
|
|
||||||
return 0, 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Outbound) AttachReturn(returnPath tun.Return) error {
|
func (o *Outbound) AttachReturn(returnPath tun.Return) error {
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
//go:build windows && (amd64 || 386)
|
|
||||||
|
|
||||||
package bridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SIO_ACQUIRE_PORT_RESERVATION = _WSAIOW(IOC_VENDOR, 100):
|
|
||||||
// IOC_IN | IOC_VENDOR | 100. Despite the write-only direction code, the
|
|
||||||
// reservation result is written to the WSAIoctl output buffer; using
|
|
||||||
// _WSAIORW instead is rejected with WSAEOPNOTSUPP.
|
|
||||||
const sioAcquirePortReservation uint32 = 0x80000000 | 0x18000000 | 100
|
|
||||||
|
|
||||||
// portReservation holds a runtime port block acquired from the host TCP/IP
|
|
||||||
// stack. Runtime reservation records are protocol- and family-agnostic:
|
|
||||||
// one reservation excludes the block from ephemeral auto-assignment for
|
|
||||||
// TCP and UDP sockets of both address families (and a specific reservation
|
|
||||||
// request for numbers covered by any existing record fails with
|
|
||||||
// WSAEADDRINUSE, whatever its protocol). Explicit binds inside the block
|
|
||||||
// are rejected for the reserving protocol but still allowed for others.
|
|
||||||
// Closing the socket releases the reservation.
|
|
||||||
type portReservation struct {
|
|
||||||
socket windows.Handle
|
|
||||||
startPort uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func acquirePortReservation(family, socketType, protocol int, count uint16) (*portReservation, error) {
|
|
||||||
socket, err := windows.Socket(family, socketType, protocol)
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "create reservation socket")
|
|
||||||
}
|
|
||||||
// INET_PORT_RANGE { USHORT StartPort; USHORT NumberOfPorts; }.
|
|
||||||
// StartPort 0 requests a runtime (wildcard) reservation.
|
|
||||||
var in [4]byte
|
|
||||||
binary.LittleEndian.PutUint16(in[0:2], 0)
|
|
||||||
binary.LittleEndian.PutUint16(in[2:4], count)
|
|
||||||
// INET_PORT_RESERVATION_INSTANCE {
|
|
||||||
// INET_PORT_RESERVATION { USHORT StartPort; USHORT NumberOfPorts; };
|
|
||||||
// INET_PORT_RESERVATION_TOKEN { ULONG64 Token; };
|
|
||||||
// } — the ULONG64 forces 8-byte alignment, so Token sits at offset 8.
|
|
||||||
var out [16]byte
|
|
||||||
var returned uint32
|
|
||||||
err = windows.WSAIoctl(socket, sioAcquirePortReservation,
|
|
||||||
&in[0], uint32(len(in)), &out[0], uint32(len(out)), &returned, nil, 0)
|
|
||||||
if err != nil {
|
|
||||||
windows.Closesocket(socket)
|
|
||||||
return nil, E.Cause(err, "acquire port reservation")
|
|
||||||
}
|
|
||||||
// StartPort is returned in network byte order (as documented for
|
|
||||||
// INET_PORT_RANGE); NumberOfPorts is a plain host-order count.
|
|
||||||
startPort := binary.BigEndian.Uint16(out[0:2])
|
|
||||||
reservedCount := binary.LittleEndian.Uint16(out[2:4])
|
|
||||||
if startPort == 0 || reservedCount < count {
|
|
||||||
windows.Closesocket(socket)
|
|
||||||
return nil, E.New("acquire port reservation: stack returned ", reservedCount, " of ", count, " ports")
|
|
||||||
}
|
|
||||||
return &portReservation{
|
|
||||||
socket: socket,
|
|
||||||
startPort: startPort,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *portReservation) Close() {
|
|
||||||
if r == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
windows.Closesocket(r.socket)
|
|
||||||
}
|
|
||||||
@@ -250,6 +250,10 @@ func (c *ClientEndpoint) PortMTU() uint32 {
|
|||||||
return c.device.PortMTU()
|
return c.device.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientEndpoint) UpstreamPort() any {
|
||||||
|
return c.device
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientEndpoint) AttachReturn(returnPath tun.Return) error {
|
func (c *ClientEndpoint) AttachReturn(returnPath tun.Return) error {
|
||||||
return c.device.AttachReturn(returnPath)
|
return c.device.AttachReturn(returnPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,10 @@ func (s *ServerEndpoint) PortMTU() uint32 {
|
|||||||
return s.device.PortMTU()
|
return s.device.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServerEndpoint) UpstreamPort() any {
|
||||||
|
return s.device
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ServerEndpoint) AttachReturn(returnPath tun.Return) error {
|
func (s *ServerEndpoint) AttachReturn(returnPath tun.Return) error {
|
||||||
return s.device.AttachReturn(returnPath)
|
return s.device.AttachReturn(returnPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,10 @@ func (e *Endpoint) PortMTU() uint32 {
|
|||||||
return e.device.PortMTU()
|
return e.device.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Endpoint) UpstreamPort() any {
|
||||||
|
return e.device
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Endpoint) AttachReturn(returnPath tun.Return) error {
|
func (e *Endpoint) AttachReturn(returnPath tun.Return) error {
|
||||||
return e.device.AttachReturn(returnPath)
|
return e.device.AttachReturn(returnPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -739,6 +739,10 @@ func (c *ClientEndpoint) PortMTU() uint32 {
|
|||||||
return c.device.PortMTU()
|
return c.device.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientEndpoint) UpstreamPort() any {
|
||||||
|
return c.device
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientEndpoint) AttachReturn(returnPath tun.Return) error {
|
func (c *ClientEndpoint) AttachReturn(returnPath tun.Return) error {
|
||||||
return c.device.AttachReturn(returnPath)
|
return c.device.AttachReturn(returnPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -655,6 +655,10 @@ func (s *ServerEndpoint) PortMTU() uint32 {
|
|||||||
return s.device.PortMTU()
|
return s.device.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServerEndpoint) UpstreamPort() any {
|
||||||
|
return s.device
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ServerEndpoint) AttachReturn(returnPath tun.Return) error {
|
func (s *ServerEndpoint) AttachReturn(returnPath tun.Return) error {
|
||||||
return s.device.AttachReturn(returnPath)
|
return s.device.AttachReturn(returnPath)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,13 @@ func (t *Endpoint) PortMTU() uint32 {
|
|||||||
return uint32(tsTUN.DefaultTUNMTU())
|
return uint32(tsTUN.DefaultTUNMTU())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Endpoint) UpstreamPort() any {
|
||||||
|
if !t.started.Load() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.stack
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Endpoint) JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort, firstPacket []byte) tun.FlowVerdict {
|
func (t *Endpoint) JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort, firstPacket []byte) tun.FlowVerdict {
|
||||||
inet4Address, inet6Address := t.PortAddresses()
|
inet4Address, inet6Address := t.PortAddresses()
|
||||||
if destination.Addr() == inet4Address || destination.Addr() == inet6Address {
|
if destination.Addr() == inet4Address || destination.Addr() == inet6Address {
|
||||||
|
|||||||
@@ -207,6 +207,10 @@ func (w *Endpoint) PortMTU() uint32 {
|
|||||||
return w.endpoint.PortMTU()
|
return w.endpoint.PortMTU()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Endpoint) UpstreamPort() any {
|
||||||
|
return w.endpoint
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Endpoint) AttachReturn(returnPath tun.Return) error {
|
func (w *Endpoint) AttachReturn(returnPath tun.Return) error {
|
||||||
return w.endpoint.AttachReturn(returnPath)
|
return w.endpoint.AttachReturn(returnPath)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -538,7 +538,7 @@ func (r *Router) preMatchFlow(ctx context.Context, metadata *adapter.InboundCont
|
|||||||
metadata.OutboundChain = chain
|
metadata.OutboundChain = chain
|
||||||
metadataCopy := *metadata
|
metadataCopy := *metadata
|
||||||
result.NewTracker = func() tun.FlowTracker {
|
result.NewTracker = func() tun.FlowTracker {
|
||||||
r.logger.InfoContext(ctx, "pre-match: forward ", metadataCopy.Network, " connection from ", metadataCopy.Source.AddrString(), " to ", metadataCopy.Destination.AddrString(), " via outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
r.logger.InfoContext(ctx, "pre-match: forward ", metadataCopy.Network, " connection from ", metadataCopy.Source, " to ", metadataCopy.Destination, " via outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||||
flowTrackers := make([]tun.FlowTracker, 0, len(r.trackers)+2)
|
flowTrackers := make([]tun.FlowTracker, 0, len(r.trackers)+2)
|
||||||
flowTrackers = append(flowTrackers, newFlowLogger(ctx, r.logger, metadataCopy, outbound))
|
flowTrackers = append(flowTrackers, newFlowLogger(ctx, r.logger, metadataCopy, outbound))
|
||||||
flowInterrupter := newFlowInterrupter(chain)
|
flowInterrupter := newFlowInterrupter(chain)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ type Device interface {
|
|||||||
PortMTU() uint32
|
PortMTU() uint32
|
||||||
AttachReturn(returnPath tun.Return) error
|
AttachReturn(returnPath tun.Return) error
|
||||||
DetachReturn(returnPath tun.Return) error
|
DetachReturn(returnPath tun.Return) error
|
||||||
|
UpstreamPort() any
|
||||||
FrontHeadroom() int
|
FrontHeadroom() int
|
||||||
NewOutboundQueue(handler func(packetBuffers []*buf.Buffer)) *tun.OutboundQueue
|
NewOutboundQueue(handler func(packetBuffers []*buf.Buffer)) *tun.OutboundQueue
|
||||||
Close() error
|
Close() error
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ func (d *stackDevice) PortMTU() uint32 {
|
|||||||
return d.options.MTU
|
return d.options.MTU
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *stackDevice) UpstreamPort() any {
|
||||||
|
return d.stack
|
||||||
|
}
|
||||||
|
|
||||||
func (d *stackDevice) NewOutboundQueue(handler func(packetBuffers []*buf.Buffer)) *tun.OutboundQueue {
|
func (d *stackDevice) NewOutboundQueue(handler func(packetBuffers []*buf.Buffer)) *tun.OutboundQueue {
|
||||||
return d.memoryTun.NewOutboundQueue(handler)
|
return d.memoryTun.NewOutboundQueue(handler)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
|
"github.com/sagernet/sing-box/common/kernelports"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing-tun/gtcpip/header"
|
"github.com/sagernet/sing-tun/gtcpip/header"
|
||||||
@@ -29,6 +30,7 @@ type systemDevice struct {
|
|||||||
options Options
|
options Options
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
device tun.Tun
|
device tun.Tun
|
||||||
|
ports *kernelports.Pool
|
||||||
inet4Address netip.Addr
|
inet4Address netip.Addr
|
||||||
inet6Address netip.Addr
|
inet6Address netip.Addr
|
||||||
closed bool
|
closed bool
|
||||||
@@ -58,7 +60,16 @@ func newSystemDevice(options Options) (*systemDevice, error) {
|
|||||||
func (d *systemDevice) Start() error {
|
func (d *systemDevice) Start() error {
|
||||||
d.stateAccess.Lock()
|
d.stateAccess.Lock()
|
||||||
defer d.stateAccess.Unlock()
|
defer d.stateAccess.Unlock()
|
||||||
return d.startLocked()
|
err := d.startLocked()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ports, err := kernelports.New()
|
||||||
|
if err != nil {
|
||||||
|
d.options.Logger.Warn(E.Cause(err, "reserve selector ports"))
|
||||||
|
}
|
||||||
|
d.ports = ports
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *systemDevice) startLocked() error {
|
func (d *systemDevice) startLocked() error {
|
||||||
@@ -363,15 +374,28 @@ func (d *systemDevice) PortMTU() uint32 {
|
|||||||
return d.options.MTU
|
return d.options.MTU
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *systemDevice) UpstreamPort() any {
|
||||||
|
d.stateAccess.RLock()
|
||||||
|
defer d.stateAccess.RUnlock()
|
||||||
|
if d.ports == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.ports
|
||||||
|
}
|
||||||
|
|
||||||
func (d *systemDevice) Close() error {
|
func (d *systemDevice) Close() error {
|
||||||
d.stateAccess.Lock()
|
d.stateAccess.Lock()
|
||||||
defer d.stateAccess.Unlock()
|
defer d.stateAccess.Unlock()
|
||||||
d.closed = true
|
d.closed = true
|
||||||
if d.device == nil {
|
var err error
|
||||||
return nil
|
if d.device != nil {
|
||||||
|
err = d.device.Close()
|
||||||
|
d.device = nil
|
||||||
|
}
|
||||||
|
if d.ports != nil {
|
||||||
|
err = E.Errors(err, d.ports.Close())
|
||||||
|
d.ports = nil
|
||||||
}
|
}
|
||||||
err := d.device.Close()
|
|
||||||
d.device = nil
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type Device interface {
|
|||||||
SetDevice(device *device.Device, peers []*device.Peer)
|
SetDevice(device *device.Device, peers []*device.Peer)
|
||||||
Inet4Address() netip.Addr
|
Inet4Address() netip.Addr
|
||||||
Inet6Address() netip.Addr
|
Inet6Address() netip.Addr
|
||||||
|
UpstreamPort() any
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceOptions struct {
|
type DeviceOptions struct {
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ func (w *stackDevice) Inet6Address() netip.Addr {
|
|||||||
return w.inet6Address
|
return w.inet6Address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *stackDevice) UpstreamPort() any {
|
||||||
|
return w.stack
|
||||||
|
}
|
||||||
|
|
||||||
func (w *stackDevice) SetDevice(device *device.Device, peers []*device.Peer) {
|
func (w *stackDevice) SetDevice(device *device.Device, peers []*device.Peer) {
|
||||||
w.router.setPeers(device.AllowedIPs(), peers)
|
w.router.setPeers(device.AllowedIPs(), peers)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/kernelports"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
@@ -24,6 +27,7 @@ type systemDevice struct {
|
|||||||
options DeviceOptions
|
options DeviceOptions
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
device tun.Tun
|
device tun.Tun
|
||||||
|
ports atomic.Pointer[kernelports.Pool]
|
||||||
batchDevice tun.LinuxTUN
|
batchDevice tun.LinuxTUN
|
||||||
events chan wgTun.Event
|
events chan wgTun.Event
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
@@ -64,6 +68,14 @@ func (w *systemDevice) Inet6Address() netip.Addr {
|
|||||||
func (w *systemDevice) SetDevice(device *device.Device, peers []*device.Peer) {
|
func (w *systemDevice) SetDevice(device *device.Device, peers []*device.Peer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *systemDevice) UpstreamPort() any {
|
||||||
|
ports := w.ports.Load()
|
||||||
|
if ports == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ports
|
||||||
|
}
|
||||||
|
|
||||||
func (w *systemDevice) Start() error {
|
func (w *systemDevice) Start() error {
|
||||||
networkManager := service.FromContext[adapter.NetworkManager](w.options.Context)
|
networkManager := service.FromContext[adapter.NetworkManager](w.options.Context)
|
||||||
tunOptions := tun.Options{
|
tunOptions := tun.Options{
|
||||||
@@ -99,6 +111,12 @@ func (w *systemDevice) Start() error {
|
|||||||
tunInterface.Close()
|
tunInterface.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
ports, err := kernelports.New()
|
||||||
|
if err != nil {
|
||||||
|
w.options.Logger.Warn(E.Cause(err, "reserve selector ports"))
|
||||||
|
} else {
|
||||||
|
w.ports.Store(ports)
|
||||||
|
}
|
||||||
w.options.Logger.Info("started at ", w.options.Name)
|
w.options.Logger.Info("started at ", w.options.Name)
|
||||||
w.device = tunInterface
|
w.device = tunInterface
|
||||||
batchTUN, isBatchTUN := tunInterface.(tun.LinuxTUN)
|
batchTUN, isBatchTUN := tunInterface.(tun.LinuxTUN)
|
||||||
@@ -168,6 +186,10 @@ func (w *systemDevice) Close() error {
|
|||||||
if w.device != nil {
|
if w.device != nil {
|
||||||
err = w.device.Close()
|
err = w.device.Close()
|
||||||
}
|
}
|
||||||
|
ports := w.ports.Load()
|
||||||
|
if ports != nil {
|
||||||
|
err = E.Errors(err, ports.Close())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ func (e *Endpoint) PortMTU() uint32 {
|
|||||||
return e.options.MTU
|
return e.options.MTU
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Endpoint) UpstreamPort() any {
|
||||||
|
return e.tunDevice
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Endpoint) WritePackets(packets [][]byte) error {
|
func (e *Endpoint) WritePackets(packets [][]byte) error {
|
||||||
wgDevice := e.device.Load()
|
wgDevice := e.device.Load()
|
||||||
if wgDevice == nil {
|
if wgDevice == nil {
|
||||||
|
|||||||
Reference in new issue
Block a user