初版功能完成
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// Package clientwg embeds wireguard-go around the single RemLink Wintun.
|
||||
package clientwg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
var _ tun.Device = (*MuxTun)(nil)
|
||||
|
||||
// MuxTun is the deliberately thin Phase 1 wrapper around the real Wintun.
|
||||
// Later phases add Read-side CIDR classification without changing this surface.
|
||||
type MuxTun struct {
|
||||
base tun.Device
|
||||
router atomic.Pointer[PacketMux]
|
||||
writeMu sync.Mutex
|
||||
closeOnce sync.Once
|
||||
closeErr error
|
||||
}
|
||||
|
||||
// NewMuxTun wraps a live TUN device without changing its semantics.
|
||||
func NewMuxTun(base tun.Device) *MuxTun {
|
||||
return &MuxTun{base: base}
|
||||
}
|
||||
|
||||
func (m *MuxTun) File() *os.File {
|
||||
return m.base.File()
|
||||
}
|
||||
|
||||
func (m *MuxTun) Read(bufs [][]byte, sizes []int, offset int) (int, error) {
|
||||
router := m.router.Load()
|
||||
if router == nil {
|
||||
return m.base.Read(bufs, sizes, offset)
|
||||
}
|
||||
if len(bufs) == 0 || len(sizes) < len(bufs) || offset < 0 {
|
||||
return 0, errors.New("invalid MuxTun read buffers")
|
||||
}
|
||||
for {
|
||||
count, readErr := m.base.Read(bufs, sizes, offset)
|
||||
if count < 0 || count > len(bufs) {
|
||||
return 0, errors.New("base TUN returned invalid batch count")
|
||||
}
|
||||
overlayCount := 0
|
||||
for index := 0; index < count; index++ {
|
||||
size := sizes[index]
|
||||
if size < 0 || offset+size > len(bufs[index]) {
|
||||
return 0, errors.New("base TUN returned invalid packet size")
|
||||
}
|
||||
packet := bufs[index][offset : offset+size]
|
||||
class, sink := router.route(packet)
|
||||
switch class {
|
||||
case PacketOverlay:
|
||||
if overlayCount != index {
|
||||
if offset+size > len(bufs[overlayCount]) {
|
||||
return 0, errors.New("destination batch buffer is too small")
|
||||
}
|
||||
copy(bufs[overlayCount][offset:offset+size], packet)
|
||||
}
|
||||
sizes[overlayCount] = size
|
||||
overlayCount++
|
||||
case PacketRemote:
|
||||
if sink != nil {
|
||||
owned := append([]byte(nil), packet...)
|
||||
if !sink.Enqueue(owned) {
|
||||
router.recordDrop(DropEvent{Reason: DropRemoteQueueFull})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if overlayCount > 0 || readErr != nil {
|
||||
return overlayCount, readErr
|
||||
}
|
||||
// A batch containing only Remote/dropped packets is consumed here. Read
|
||||
// again instead of returning 0,nil to wireguard-go and spinning it.
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MuxTun) Write(bufs [][]byte, offset int) (int, error) {
|
||||
m.writeMu.Lock()
|
||||
defer m.writeMu.Unlock()
|
||||
return m.base.Write(bufs, offset)
|
||||
}
|
||||
|
||||
// SetPacketMux enables or replaces Engineer outbound classification. A nil
|
||||
// value restores Phase 1 passthrough behavior.
|
||||
func (m *MuxTun) SetPacketMux(router *PacketMux) { m.router.Store(router) }
|
||||
|
||||
// InjectInbound serializes Site Session replies with wireguard-go writes.
|
||||
func (m *MuxTun) InjectInbound(packet []byte) error {
|
||||
m.writeMu.Lock()
|
||||
defer m.writeMu.Unlock()
|
||||
_, err := m.base.Write([][]byte{packet}, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MuxTun) MTU() (int, error) {
|
||||
return m.base.MTU()
|
||||
}
|
||||
|
||||
func (m *MuxTun) Name() (string, error) {
|
||||
return m.base.Name()
|
||||
}
|
||||
|
||||
func (m *MuxTun) Events() <-chan tun.Event {
|
||||
return m.base.Events()
|
||||
}
|
||||
|
||||
func (m *MuxTun) Close() error {
|
||||
m.closeOnce.Do(func() {
|
||||
m.closeErr = m.base.Close()
|
||||
})
|
||||
return m.closeErr
|
||||
}
|
||||
|
||||
func (m *MuxTun) BatchSize() int {
|
||||
return m.base.BatchSize()
|
||||
}
|
||||
Reference in New Issue
Block a user