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 <danderson@tailscale.com>
This commit is contained in:
David Anderson 2021-03-26 23:13:20 -07:00
parent 82ab7972f4
commit 016de16b2e
7 changed files with 58 additions and 61 deletions

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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:

View File

@ -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 ||

View File

@ -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()...),

View File

@ -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{},
}