From 016de16b2e7db78d0d312bf5740edf87f761e01e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 26 Mar 2021 23:13:20 -0700 Subject: [PATCH] net/tstun: rename TUN to Wrapper. The tstun packagen contains both constructors for generic tun Devices, and a wrapper that provides additional functionality. Signed-off-by: David Anderson --- net/tstun/wrap.go | 73 +++++++++++++--------------- net/tstun/wrap_test.go | 16 +++--- wgengine/magicsock/magicsock_test.go | 4 +- wgengine/netstack/netstack.go | 6 +-- wgengine/pendopen.go | 4 +- wgengine/userspace.go | 14 +++--- wgengine/userspace_test.go | 2 +- 7 files changed, 58 insertions(+), 61 deletions(-) diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go index 0f0725eb3..70225c52e 100644 --- a/net/tstun/wrap.go +++ b/net/tstun/wrap.go @@ -31,11 +31,11 @@ const maxBufferSize = device.MaxMessageSize const PacketStartOffset = device.MessageTransportHeaderSize // MaxPacketSize is the maximum size (in bytes) -// of a packet that can be injected into a tstun.TUN. +// of a packet that can be injected into a tstun.Wrapper. const MaxPacketSize = device.MaxContentSize var ( - // ErrClosed is returned when attempting an operation on a closed TUN. + // ErrClosed is returned when attempting an operation on a closed Wrapper. ErrClosed = errors.New("device closed") // ErrFiltered is returned when the acted-on packet is rejected by a filter. ErrFiltered = errors.New("packet dropped by filter") @@ -52,17 +52,14 @@ var ( // do not escape through {Pre,Post}Filter{In,Out}. var parsedPacketPool = sync.Pool{New: func() interface{} { return new(packet.Parsed) }} -// FilterFunc is a packet-filtering function with access to the TUN device. +// FilterFunc is a packet-filtering function with access to the Wrapper device. // It must not hold onto the packet struct, as its backing storage will be reused. -type FilterFunc func(*packet.Parsed, *TUN) filter.Response +type FilterFunc func(*packet.Parsed, *Wrapper) filter.Response -// TUN wraps a tun.Device from wireguard-go, -// augmenting it with filtering and packet injection. -// All the added work happens in Read and Write: -// the other methods delegate to the underlying tdev. -type TUN struct { +// Wrapper augments a tun.Device with packet filtering and injection. +type Wrapper struct { logf logger.Logf - // tdev is the underlying TUN device. + // tdev is the underlying Wrapper device. tdev tun.Device closeOnce sync.Once @@ -116,8 +113,8 @@ type TUN struct { disableFilter bool } -func WrapTUN(logf logger.Logf, tdev tun.Device) *TUN { - tun := &TUN{ +func Wrap(logf logger.Logf, tdev tun.Device) *Wrapper { + tun := &Wrapper{ logf: logger.WithPrefix(logf, "tstun: "), tdev: tdev, // bufferConsumed is conceptually a condition variable: @@ -140,12 +137,12 @@ func WrapTUN(logf logger.Logf, tdev tun.Device) *TUN { // SetDestIPActivityFuncs sets a map of funcs to run per packet // destination (the map keys). // -// The map ownership passes to the TUN. It must be non-nil. -func (t *TUN) SetDestIPActivityFuncs(m map[netaddr.IP]func()) { +// The map ownership passes to the Wrapper. It must be non-nil. +func (t *Wrapper) SetDestIPActivityFuncs(m map[netaddr.IP]func()) { t.destIPActivity.Store(m) } -func (t *TUN) Close() error { +func (t *Wrapper) Close() error { var err error t.closeOnce.Do(func() { // Other channels need not be closed: poll will exit gracefully after this. @@ -156,30 +153,30 @@ func (t *TUN) Close() error { return err } -func (t *TUN) Events() chan tun.Event { +func (t *Wrapper) Events() chan tun.Event { return t.tdev.Events() } -func (t *TUN) File() *os.File { +func (t *Wrapper) File() *os.File { return t.tdev.File() } -func (t *TUN) Flush() error { +func (t *Wrapper) Flush() error { return t.tdev.Flush() } -func (t *TUN) MTU() (int, error) { +func (t *Wrapper) MTU() (int, error) { return t.tdev.MTU() } -func (t *TUN) Name() (string, error) { +func (t *Wrapper) Name() (string, error) { return t.tdev.Name() } // poll polls t.tdev.Read, placing the oldest unconsumed packet into t.buffer. // This is needed because t.tdev.Read in general may block (it does on Windows), // so packets may be stuck in t.outbound if t.Read called t.tdev.Read directly. -func (t *TUN) poll() { +func (t *Wrapper) poll() { for { select { case <-t.closed: @@ -189,7 +186,7 @@ func (t *TUN) poll() { } // Read may use memory in t.buffer before PacketStartOffset for mandatory headers. - // This is the rationale behind the tun.TUN.{Read,Write} interfaces + // This is the rationale behind the tun.Wrapper.{Read,Write} interfaces // and the reason t.buffer has size MaxMessageSize and not MaxContentSize. n, err := t.tdev.Read(t.buffer[:], PacketStartOffset) if err != nil { @@ -221,7 +218,7 @@ func (t *TUN) poll() { var magicDNSIPPort = netaddr.MustParseIPPort("100.100.100.100:0") -func (t *TUN) filterOut(p *packet.Parsed) filter.Response { +func (t *Wrapper) filterOut(p *packet.Parsed) filter.Response { // Fake ICMP echo responses to MagicDNS (100.100.100.100). if p.IsEchoRequest() && p.Dst == magicDNSIPPort { header := p.ICMP4Header() @@ -257,7 +254,7 @@ func (t *TUN) filterOut(p *packet.Parsed) filter.Response { } // noteActivity records that there was a read or write at the current time. -func (t *TUN) noteActivity() { +func (t *Wrapper) noteActivity() { atomic.StoreInt64(&t.lastActivityAtomic, time.Now().Unix()) } @@ -265,12 +262,12 @@ func (t *TUN) noteActivity() { // // Its value is only accurate to roughly second granularity. // If there's never been activity, the duration is since 1970. -func (t *TUN) IdleDuration() time.Duration { +func (t *Wrapper) IdleDuration() time.Duration { sec := atomic.LoadInt64(&t.lastActivityAtomic) return time.Since(time.Unix(sec, 0)) } -func (t *TUN) Read(buf []byte, offset int) (int, error) { +func (t *Wrapper) Read(buf []byte, offset int) (int, error) { var n int wasInjectedPacket := false @@ -321,7 +318,7 @@ func (t *TUN) Read(buf []byte, offset int) (int, error) { return n, nil } -func (t *TUN) filterIn(buf []byte) filter.Response { +func (t *Wrapper) filterIn(buf []byte) filter.Response { p := parsedPacketPool.Get().(*packet.Parsed) defer parsedPacketPool.Put(p) p.Decode(buf) @@ -388,7 +385,7 @@ func (t *TUN) filterIn(buf []byte) filter.Response { // Write accepts an incoming packet. The packet begins at buf[offset:], // like wireguard-go/tun.Device.Write. -func (t *TUN) Write(buf []byte, offset int) (int, error) { +func (t *Wrapper) Write(buf []byte, offset int) (int, error) { if !t.disableFilter { res := t.filterIn(buf[offset:]) if res == filter.DropSilently { @@ -403,16 +400,16 @@ func (t *TUN) Write(buf []byte, offset int) (int, error) { return t.tdev.Write(buf, offset) } -func (t *TUN) GetFilter() *filter.Filter { +func (t *Wrapper) GetFilter() *filter.Filter { filt, _ := t.filter.Load().(*filter.Filter) return filt } -func (t *TUN) SetFilter(filt *filter.Filter) { +func (t *Wrapper) SetFilter(filt *filter.Filter) { t.filter.Store(filt) } -// InjectInboundDirect makes the TUN device behave as if a packet +// InjectInboundDirect makes the Wrapper device behave as if a packet // with the given contents was received from the network. // It blocks and does not take ownership of the packet. // The injected packet will not pass through inbound filters. @@ -420,7 +417,7 @@ func (t *TUN) SetFilter(filt *filter.Filter) { // The packet contents are to start at &buf[offset]. // offset must be greater or equal to PacketStartOffset. // The space before &buf[offset] will be used by Wireguard. -func (t *TUN) InjectInboundDirect(buf []byte, offset int) error { +func (t *Wrapper) InjectInboundDirect(buf []byte, offset int) error { if len(buf) > MaxPacketSize { return errPacketTooBig } @@ -439,7 +436,7 @@ func (t *TUN) InjectInboundDirect(buf []byte, offset int) error { // InjectInboundCopy takes a packet without leading space, // reallocates it to conform to the InjectInboundDirect interface // and calls InjectInboundDirect on it. Injecting a nil packet is a no-op. -func (t *TUN) InjectInboundCopy(packet []byte) error { +func (t *Wrapper) InjectInboundCopy(packet []byte) error { // We duplicate this check from InjectInboundDirect here // to avoid wasting an allocation on an oversized packet. if len(packet) > MaxPacketSize { @@ -455,7 +452,7 @@ func (t *TUN) InjectInboundCopy(packet []byte) error { return t.InjectInboundDirect(buf, PacketStartOffset) } -func (t *TUN) injectOutboundPong(pp *packet.Parsed, req packet.TSMPPingRequest) { +func (t *Wrapper) injectOutboundPong(pp *packet.Parsed, req packet.TSMPPingRequest) { pong := packet.TSMPPongReply{ Data: req.Data, } @@ -475,12 +472,12 @@ func (t *TUN) injectOutboundPong(pp *packet.Parsed, req packet.TSMPPingRequest) t.InjectOutbound(packet.Generate(pong, nil)) } -// InjectOutbound makes the TUN device behave as if a packet +// InjectOutbound makes the Wrapper device behave as if a packet // with the given contents was sent to the network. // It does not block, but takes ownership of the packet. // The injected packet will not pass through outbound filters. // Injecting an empty packet is a no-op. -func (t *TUN) InjectOutbound(packet []byte) error { +func (t *Wrapper) InjectOutbound(packet []byte) error { if len(packet) > MaxPacketSize { return errPacketTooBig } @@ -495,7 +492,7 @@ func (t *TUN) InjectOutbound(packet []byte) error { } } -// Unwrap returns the underlying TUN device. -func (t *TUN) Unwrap() tun.Device { +// Unwrap returns the underlying tun.Device. +func (t *Wrapper) Unwrap() tun.Device { return t.tdev } diff --git a/net/tstun/wrap_test.go b/net/tstun/wrap_test.go index 2fe82cf06..4032b168b 100644 --- a/net/tstun/wrap_test.go +++ b/net/tstun/wrap_test.go @@ -106,7 +106,7 @@ func netports(netPorts ...string) (ret []filter.NetPortRange) { return ret } -func setfilter(logf logger.Logf, tun *TUN) { +func setfilter(logf logger.Logf, tun *Wrapper) { protos := []ipproto.Proto{ ipproto.TCP, ipproto.UDP, @@ -120,9 +120,9 @@ func setfilter(logf logger.Logf, tun *TUN) { tun.SetFilter(filter.New(matches, sb.IPSet(), sb.IPSet(), nil, logf)) } -func newChannelTUN(logf logger.Logf, secure bool) (*tuntest.ChannelTUN, *TUN) { +func newChannelTUN(logf logger.Logf, secure bool) (*tuntest.ChannelTUN, *Wrapper) { chtun := tuntest.NewChannelTUN() - tun := WrapTUN(logf, chtun.TUN()) + tun := Wrap(logf, chtun.TUN()) if secure { setfilter(logf, tun) } else { @@ -131,9 +131,9 @@ func newChannelTUN(logf logger.Logf, secure bool) (*tuntest.ChannelTUN, *TUN) { return chtun, tun } -func newFakeTUN(logf logger.Logf, secure bool) (*fakeTUN, *TUN) { +func newFakeTUN(logf logger.Logf, secure bool) (*fakeTUN, *Wrapper) { ftun := NewFake() - tun := WrapTUN(logf, ftun) + tun := Wrap(logf, ftun) if secure { setfilter(logf, tun) } else { @@ -274,7 +274,7 @@ func TestFilter(t *testing.T) { {"good_packet_out", out, false, udp4("1.2.3.4", "5.6.7.8", 98, 98)}, } - // A reader on the other end of the TUN. + // A reader on the other end of the tun. go func() { var recvbuf []byte for { @@ -377,11 +377,11 @@ func BenchmarkWrite(b *testing.B) { } func TestAtomic64Alignment(t *testing.T) { - off := unsafe.Offsetof(TUN{}.lastActivityAtomic) + off := unsafe.Offsetof(Wrapper{}.lastActivityAtomic) if off%8 != 0 { t.Errorf("offset %v not 8-byte aligned", off) } - c := new(TUN) + c := new(Wrapper) atomic.StoreInt64(&c.lastActivityAtomic, 123) } diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 1e4d00b19..2e7e29635 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -130,7 +130,7 @@ type magicStack struct { epCh chan []string // endpoint updates produced by this peer conn *Conn // the magicsock itself tun *tuntest.ChannelTUN // TUN device to send/receive packets - tsTun *tstun.TUN // wrapped tun that implements filtering and wgengine hooks + tsTun *tstun.Wrapper // wrapped tun that implements filtering and wgengine hooks dev *device.Device // the wireguard-go Device that connects the previous things wgLogger *wglog.Logger // wireguard-go log wrapper } @@ -166,7 +166,7 @@ func newMagicStack(t testing.TB, logf logger.Logf, l nettype.PacketListener, der } tun := tuntest.NewChannelTUN() - tsTun := tstun.WrapTUN(logf, tun.TUN()) + tsTun := tstun.Wrap(logf, tun.TUN()) tsTun.SetFilter(filter.NewAllowAllForTest(logf)) wgLogger := wglog.NewLogger(logf) diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go index cceb10eb8..ef3c24b1d 100644 --- a/wgengine/netstack/netstack.go +++ b/wgengine/netstack/netstack.go @@ -48,7 +48,7 @@ const debugNetstack = false type Impl struct { ipstack *stack.Stack linkEP *channel.Endpoint - tundev *tstun.TUN + tundev *tstun.Wrapper e wgengine.Engine mc *magicsock.Conn logf logger.Logf @@ -61,7 +61,7 @@ const nicID = 1 const mtu = 1500 // Create creates and populates a new Impl. -func Create(logf logger.Logf, tundev *tstun.TUN, e wgengine.Engine, mc *magicsock.Conn) (*Impl, error) { +func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magicsock.Conn) (*Impl, error) { if mc == nil { return nil, errors.New("nil magicsock.Conn") } @@ -297,7 +297,7 @@ func (ns *Impl) injectOutbound() { } } -func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.TUN) filter.Response { +func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.Wrapper) filter.Response { var pn tcpip.NetworkProtocolNumber switch p.IPVersion { case 4: diff --git a/wgengine/pendopen.go b/wgengine/pendopen.go index f18e12e9f..2951a0c7e 100644 --- a/wgengine/pendopen.go +++ b/wgengine/pendopen.go @@ -66,7 +66,7 @@ func (e *userspaceEngine) noteFlowProblemFromPeer(f flowtrack.Tuple, problem pac of.problem = problem } -func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.TUN) (res filter.Response) { +func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.Wrapper) (res filter.Response) { res = filter.Accept // always if pp.IPProto == ipproto.TSMP { @@ -99,7 +99,7 @@ func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.TUN) return } -func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.TUN) (res filter.Response) { +func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.Wrapper) (res filter.Response) { res = filter.Accept // always if pp.IPVersion == 0 || diff --git a/wgengine/userspace.go b/wgengine/userspace.go index f72e8640f..01ae3cc8b 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -80,7 +80,7 @@ type userspaceEngine struct { reqCh chan struct{} waitCh chan struct{} // chan is closed when first Close call completes; contrast with closing bool timeNow func() time.Time - tundev *tstun.TUN + tundev *tstun.Wrapper wgdev *device.Device router router.Router resolver *dns.Resolver @@ -124,10 +124,10 @@ type userspaceEngine struct { // InternalsGetter is implemented by Engines that can export their internals. type InternalsGetter interface { - GetInternals() (*tstun.TUN, *magicsock.Conn) + GetInternals() (*tstun.Wrapper, *magicsock.Conn) } -func (e *userspaceEngine) GetInternals() (*tstun.TUN, *magicsock.Conn) { +func (e *userspaceEngine) GetInternals() (*tstun.Wrapper, *magicsock.Conn) { return e.tundev, e.magicConn } @@ -184,7 +184,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) closePool.add(r) } - tsTUNDev := tstun.WrapTUN(logf, conf.TUN) + tsTUNDev := tstun.Wrap(logf, conf.TUN) closePool.add(tsTUNDev) e := &userspaceEngine{ @@ -379,7 +379,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) } // echoRespondToAll is an inbound post-filter responding to all echo requests. -func echoRespondToAll(p *packet.Parsed, t *tstun.TUN) filter.Response { +func echoRespondToAll(p *packet.Parsed, t *tstun.Wrapper) filter.Response { if p.IsEchoRequest() { header := p.ICMP4Header() header.ToResponse() @@ -400,7 +400,7 @@ func echoRespondToAll(p *packet.Parsed, t *tstun.TUN) filter.Response { // stack, and intercepts any packets that should be handled by // tailscaled directly. Other packets are allowed to proceed into the // main ACL filter. -func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.TUN) filter.Response { +func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Response { if verdict := e.handleDNS(p, t); verdict == filter.Drop { // local DNS handled the packet. return filter.Drop @@ -429,7 +429,7 @@ func (e *userspaceEngine) isLocalAddr(ip netaddr.IP) bool { } // handleDNS is an outbound pre-filter resolving Tailscale domains. -func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.TUN) filter.Response { +func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response { if p.Dst.IP == magicDNSIP && p.Dst.Port == magicDNSPort && p.IPProto == ipproto.UDP { request := dns.Packet{ Payload: append([]byte(nil), p.Payload()...), diff --git a/wgengine/userspace_test.go b/wgengine/userspace_test.go index 6690ece01..6321fdf31 100644 --- a/wgengine/userspace_test.go +++ b/wgengine/userspace_test.go @@ -39,7 +39,7 @@ func TestNoteReceiveActivity(t *testing.T) { logf: func(format string, a ...interface{}) { fmt.Fprintf(&logBuf, format, a...) }, - tundev: new(tstun.TUN), + tundev: new(tstun.Wrapper), testMaybeReconfigHook: func() { confc <- true }, trimmedDisco: map[tailcfg.DiscoKey]bool{}, }