mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-03 02:21:58 +00:00
tsdns: initial implementation of a Tailscale DNS resolver (#396)
Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>
This commit is contained in:
committed by
GitHub
parent
5e1ee4be53
commit
511840b1f6
@@ -34,6 +34,7 @@ import (
|
||||
"tailscale.com/wgengine/monitor"
|
||||
"tailscale.com/wgengine/packet"
|
||||
"tailscale.com/wgengine/router"
|
||||
"tailscale.com/wgengine/tsdns"
|
||||
"tailscale.com/wgengine/tstun"
|
||||
)
|
||||
|
||||
@@ -54,6 +55,7 @@ type userspaceEngine struct {
|
||||
tundev *tstun.TUN
|
||||
wgdev *device.Device
|
||||
router router.Router
|
||||
resolver *tsdns.Resolver
|
||||
magicConn *magicsock.Conn
|
||||
linkMon *monitor.Mon
|
||||
|
||||
@@ -73,6 +75,28 @@ type userspaceEngine struct {
|
||||
// Lock ordering: wgLock, then mu.
|
||||
}
|
||||
|
||||
// RouterGen is the signature for a function that creates a
|
||||
// router.Router.
|
||||
type RouterGen func(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (router.Router, error)
|
||||
|
||||
type EngineConfig struct {
|
||||
// Logf is the logging function used by the engine.
|
||||
Logf logger.Logf
|
||||
// TUN is the tun device used by the engine.
|
||||
TUN tun.Device
|
||||
// RouterGen is the function used to instantiate the router.
|
||||
RouterGen RouterGen
|
||||
// ListenPort is the port on which the engine will listen.
|
||||
ListenPort uint16
|
||||
// EchoRespondToAll determines whether ICMP Echo requests incoming from Tailscale peers
|
||||
// will be intercepted and responded to, regardless of the source host.
|
||||
EchoRespondToAll bool
|
||||
// UseTailscaleDNS determines whether DNS requests for names of the form *.ipn.dev
|
||||
// directed to the designated Taislcale DNS address (see wgengine/tsdns)
|
||||
// will be intercepted and resolved by a tsdns.Resolver.
|
||||
UseTailscaleDNS bool
|
||||
}
|
||||
|
||||
type Loggify struct {
|
||||
f logger.Logf
|
||||
}
|
||||
@@ -84,8 +108,14 @@ func (l *Loggify) Write(b []byte) (int, error) {
|
||||
|
||||
func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16) (Engine, error) {
|
||||
logf("Starting userspace wireguard engine (FAKE tuntap device).")
|
||||
tundev := tstun.WrapTUN(logf, tstun.NewFakeTUN())
|
||||
return NewUserspaceEngineAdvanced(logf, tundev, router.NewFake, listenPort)
|
||||
conf := EngineConfig{
|
||||
Logf: logf,
|
||||
TUN: tstun.NewFakeTUN(),
|
||||
RouterGen: router.NewFake,
|
||||
ListenPort: listenPort,
|
||||
EchoRespondToAll: true,
|
||||
}
|
||||
return NewUserspaceEngineAdvanced(conf)
|
||||
}
|
||||
|
||||
// NewUserspaceEngine creates the named tun device and returns a
|
||||
@@ -104,38 +134,53 @@ func NewUserspaceEngine(logf logger.Logf, tunname string, listenPort uint16) (En
|
||||
return nil, err
|
||||
}
|
||||
logf("CreateTUN ok.")
|
||||
tundev := tstun.WrapTUN(logf, tun)
|
||||
|
||||
e, err := NewUserspaceEngineAdvanced(logf, tundev, router.New, listenPort)
|
||||
conf := EngineConfig{
|
||||
Logf: logf,
|
||||
TUN: tun,
|
||||
RouterGen: router.New,
|
||||
ListenPort: listenPort,
|
||||
// TODO(dmytro): plumb this down.
|
||||
UseTailscaleDNS: true,
|
||||
}
|
||||
|
||||
e, err := NewUserspaceEngineAdvanced(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, err
|
||||
}
|
||||
|
||||
// RouterGen is the signature for a function that creates a
|
||||
// router.Router.
|
||||
type RouterGen func(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (router.Router, error)
|
||||
|
||||
// NewUserspaceEngineAdvanced is like NewUserspaceEngine but takes a pre-created TUN device and allows specifing
|
||||
// a custom router constructor and listening port.
|
||||
func NewUserspaceEngineAdvanced(logf logger.Logf, tundev *tstun.TUN, routerGen RouterGen, listenPort uint16) (Engine, error) {
|
||||
return newUserspaceEngineAdvanced(logf, tundev, routerGen, listenPort)
|
||||
// NewUserspaceEngineAdvanced is like NewUserspaceEngine
|
||||
// but provides control over all config fields.
|
||||
func NewUserspaceEngineAdvanced(conf EngineConfig) (Engine, error) {
|
||||
return newUserspaceEngineAdvanced(conf)
|
||||
}
|
||||
|
||||
func newUserspaceEngineAdvanced(logf logger.Logf, tundev *tstun.TUN, routerGen RouterGen, listenPort uint16) (_ Engine, reterr error) {
|
||||
func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
|
||||
logf := conf.Logf
|
||||
|
||||
e := &userspaceEngine{
|
||||
logf: logf,
|
||||
reqCh: make(chan struct{}, 1),
|
||||
waitCh: make(chan struct{}),
|
||||
tundev: tundev,
|
||||
pingers: make(map[wgcfg.Key]*pinger),
|
||||
logf: logf,
|
||||
reqCh: make(chan struct{}, 1),
|
||||
waitCh: make(chan struct{}),
|
||||
tundev: tstun.WrapTUN(logf, conf.TUN),
|
||||
resolver: tsdns.NewResolver(logf),
|
||||
pingers: make(map[wgcfg.Key]*pinger),
|
||||
}
|
||||
e.linkState, _ = getLinkState()
|
||||
|
||||
// Respond to all pings only in fake mode.
|
||||
if conf.EchoRespondToAll {
|
||||
e.tundev.PostFilterIn = echoRespondToAll
|
||||
}
|
||||
if conf.UseTailscaleDNS {
|
||||
e.tundev.PreFilterOut = e.handleDNS
|
||||
}
|
||||
|
||||
mon, err := monitor.New(logf, func() { e.LinkChange(false) })
|
||||
if err != nil {
|
||||
tundev.Close()
|
||||
e.tundev.Close()
|
||||
return nil, err
|
||||
}
|
||||
e.linkMon = mon
|
||||
@@ -149,12 +194,12 @@ func newUserspaceEngineAdvanced(logf logger.Logf, tundev *tstun.TUN, routerGen R
|
||||
}
|
||||
magicsockOpts := magicsock.Options{
|
||||
Logf: logf,
|
||||
Port: listenPort,
|
||||
Port: conf.ListenPort,
|
||||
EndpointsFunc: endpointsFn,
|
||||
}
|
||||
e.magicConn, err = magicsock.NewConn(magicsockOpts)
|
||||
if err != nil {
|
||||
tundev.Close()
|
||||
e.tundev.Close()
|
||||
return nil, fmt.Errorf("wgengine: %v", err)
|
||||
}
|
||||
|
||||
@@ -211,7 +256,7 @@ func newUserspaceEngineAdvanced(logf logger.Logf, tundev *tstun.TUN, routerGen R
|
||||
|
||||
// Pass the underlying tun.(*NativeDevice) to the router:
|
||||
// routers do not Read or Write, but do access native interfaces.
|
||||
e.router, err = routerGen(logf, e.wgdev, e.tundev.Unwrap())
|
||||
e.router, err = conf.RouterGen(logf, e.wgdev, e.tundev.Unwrap())
|
||||
if err != nil {
|
||||
e.magicConn.Close()
|
||||
return nil, err
|
||||
@@ -256,6 +301,37 @@ func newUserspaceEngineAdvanced(logf logger.Logf, tundev *tstun.TUN, routerGen R
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// echoRespondToAll is an inbound post-filter responding to all echo requests.
|
||||
func echoRespondToAll(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
||||
if p.IsEchoRequest() {
|
||||
header := p.ICMPHeader()
|
||||
header.ToResponse()
|
||||
packet := packet.Generate(&header, p.Payload())
|
||||
t.InjectOutbound(packet)
|
||||
// We already handled it, stop.
|
||||
return filter.Drop
|
||||
}
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
// handleDNS is an outbound pre-filter resolving Tailscale domains.
|
||||
func (e *userspaceEngine) handleDNS(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
||||
if e.resolver.AcceptsPacket(p) {
|
||||
// TODO(dmytro): avoid this allocation without having tsdns know tstun quirks.
|
||||
buf := make([]byte, tstun.MaxPacketSize)
|
||||
offset := tstun.PacketStartOffset
|
||||
response, err := e.resolver.Respond(p, buf[offset:])
|
||||
if err != nil {
|
||||
e.logf("DNS resolver error: %v", err)
|
||||
} else {
|
||||
t.InjectInboundDirect(buf[:offset+len(response)], offset)
|
||||
}
|
||||
// We already handled it, stop.
|
||||
return filter.Drop
|
||||
}
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
// pinger sends ping packets for a few seconds.
|
||||
//
|
||||
// These generated packets are used to ensure we trigger the spray logic in
|
||||
@@ -447,6 +523,10 @@ func (e *userspaceEngine) SetFilter(filt *filter.Filter) {
|
||||
e.tundev.SetFilter(filt)
|
||||
}
|
||||
|
||||
func (e *userspaceEngine) SetDNSMap(dm *tsdns.Map) {
|
||||
e.resolver.SetMap(dm)
|
||||
}
|
||||
|
||||
func (e *userspaceEngine) SetStatusCallback(cb StatusCallback) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user