tailscale/wgengine/kernel.go

256 lines
6.5 KiB
Go
Raw Normal View History

2021-08-30 22:30:06 +01:00
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wgengine
import (
"errors"
"fmt"
"sync"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"inet.af/netaddr"
"tailscale.com/health"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/dns"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/types/netmap"
"tailscale.com/types/wgkey"
"tailscale.com/wgengine/filter"
"tailscale.com/wgengine/kproxy"
"tailscale.com/wgengine/magicsock"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/router"
"tailscale.com/wgengine/wgcfg"
)
type kernelEngine struct {
logf logger.Logf
magicConn *magicsock.Conn
linkMon *monitor.Mon
linkMonOwned bool // whether we created linkMon (and thus need to close it)
router router.Router
dns *dns.Manager
confListenPort uint16 // original conf.ListenPort
wg *wgctrl.Client
proxy *kproxy.Proxy
proxyMap map[tailcfg.NodeKey]netaddr.IPPort
wgLock sync.Mutex
}
func NewKernelEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) {
var closePool closeOnErrorPool
defer closePool.closeAllIfError(&reterr)
const tunName = "tailscale0" // TODO: plumb somehow for variable name
if conf.Tun != nil {
return nil, errors.New("can't use a tun interface in kernel mode")
}
if conf.Router == nil {
conf.Router = router.NewFake(logf)
}
if conf.DNS == nil {
d, err := dns.NewNoopManager()
if err != nil {
return nil, err
}
conf.DNS = d
}
e := &kernelEngine{
logf: logf,
router: conf.Router,
confListenPort: conf.ListenPort,
}
if conf.LinkMonitor != nil {
e.linkMon = conf.LinkMonitor
} else {
mon, err := monitor.New(logf)
if err != nil {
return nil, err
}
closePool.add(mon)
e.linkMon = mon
e.linkMonOwned = true
}
e.dns = dns.NewManager(logf, conf.DNS, e.linkMon, nil) // TODO: make a fwdLinkSelector
magicsockOpts := magicsock.Options{
Logf: logf,
Port: conf.ListenPort,
LinkMonitor: e.linkMon,
}
var err error
e.magicConn, err = magicsock.NewConn(magicsockOpts)
if err != nil {
return nil, fmt.Errorf("wgengine: %v", err)
}
closePool.add(e.magicConn)
e.magicConn.SetNetworkUp(true)
e.proxy, err = kproxy.New(e.magicConn)
if err != nil {
return nil, fmt.Errorf("proxy: %v", err)
}
e.wg, err = wgctrl.New()
if err != nil {
return nil, fmt.Errorf("wgctrl: %v", err)
}
closePool.add(e.wg)
err = e.wg.ConfigureDevice(tunName, wgtypes.Config{
PrivateKey: &wgtypes.Key{},
ReplacePeers: true,
Peers: []wgtypes.PeerConfig{},
})
if err != nil {
return nil, fmt.Errorf("wgctrl: initial config: %v", err)
}
if err := e.router.Up(); err != nil {
return nil, err
}
e.magicConn.Start()
return e, nil
}
func (e *kernelEngine) Reconfig(wcfg *wgcfg.Config, rcfg *router.Config, dcfg *dns.Config, dbg *tailcfg.Debug) error {
if rcfg == nil {
panic("rcfg must not be nil")
}
if dcfg == nil {
panic("dcfg must not be nil")
}
e.wgLock.Lock()
defer e.wgLock.Unlock()
peerSet := make(map[key.Public]struct{}, len(wcfg.Peers))
for _, p := range wcfg.Peers {
peerSet[key.Public(p.PublicKey)] = struct{}{}
}
if err := e.magicConn.SetPrivateKey(wgkey.Private(wcfg.PrivateKey)); err != nil {
e.logf("wgengine: Reconfig: SetPrivateKey: %v", err)
}
e.magicConn.UpdatePeers(peerSet)
e.magicConn.SetPreferredPort(e.confListenPort)
port := 4242
cfg := wgtypes.Config{
PrivateKey: (*wgtypes.Key)(&wcfg.PrivateKey),
ListenPort: &port,
ReplacePeers: true,
}
for _, p := range wcfg.Peers {
v := wgtypes.PeerConfig{
PublicKey: wgtypes.Key(p.PublicKey),
Endpoint: e.proxyMap[tailcfg.NodeKey(p.PublicKey)].UDPAddr(),
ReplaceAllowedIPs: true,
}
for _, pfx := range p.AllowedIPs {
v.AllowedIPs = append(v.AllowedIPs, *pfx.IPNet())
}
cfg.Peers = append(cfg.Peers, v)
}
if err := e.wg.ConfigureDevice("tailscale0", cfg); err != nil {
return fmt.Errorf("configuring kernel: %v", err)
}
err := e.router.Set(rcfg)
health.SetRouterHealth(err)
if err != nil {
return err
}
// TODO: set DNS, but it'll just break my machine right now, I
// mean look at the state of me.
return nil
}
func (e *kernelEngine) GetFilter() *filter.Filter { return nil }
func (e *kernelEngine) SetFilter(f *filter.Filter) {}
func (e *kernelEngine) SetStatusCallback(cb StatusCallback) {}
func (e *kernelEngine) GetLinkMonitor() *monitor.Mon { return e.linkMon }
func (e *kernelEngine) RequestStatus() {}
func (e *kernelEngine) Close() {}
func (e *kernelEngine) Wait() {}
func (e *kernelEngine) LinkChange(isExpensive bool) {}
func (e *kernelEngine) SetDERPMap(m *tailcfg.DERPMap) {
e.magicConn.SetDERPMap(m)
}
func (e *kernelEngine) SetNetworkMap(nm *netmap.NetworkMap) {
e.magicConn.SetNetworkMap(nm)
m, err := e.proxy.SetNetworkMap(nm)
if err != nil {
e.logf("MUCH SADNESS: %v", err)
}
e.wgLock.Lock()
defer e.wgLock.Unlock()
e.proxyMap = m
}
func (e *kernelEngine) AddNetworkMapCallback(cb NetworkMapCallback) (rm func()) { return func() {} }
func (e *kernelEngine) SetNetInfoCallback(cb NetInfoCallback) {}
func (e *kernelEngine) DiscoPublicKey() tailcfg.DiscoKey { return e.magicConn.DiscoPublicKey() }
func (e *kernelEngine) getStatus() (*Status, error) {
// Grab derpConns before acquiring wgLock to not violate lock ordering;
// the DERPs method acquires magicsock.Conn.mu.
// (See comment in userspaceEngine's declaration.)
derpConns := e.magicConn.DERPs()
return &Status{
LocalAddrs: []tailcfg.Endpoint{},
Peers: []ipnstate.PeerStatusLite{},
DERPs: derpConns,
}, nil
}
func (e *kernelEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
st, err := e.getStatus()
if err != nil {
e.logf("wgengine: getStatus: %v", err)
return
}
for _, ps := range st.Peers {
sb.AddPeer(key.Public(ps.NodeKey), &ipnstate.PeerStatus{
RxBytes: int64(ps.RxBytes),
TxBytes: int64(ps.TxBytes),
LastHandshake: ps.LastHandshake,
InEngine: true,
})
}
e.magicConn.UpdateStatus(sb)
}
func (e *kernelEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {}
func (e *kernelEngine) RegisterIPPortIdentity(ipp netaddr.IPPort, ip netaddr.IP) {}
func (e *kernelEngine) UnregisterIPPortIdentity(ipp netaddr.IPPort) {}
func (e *kernelEngine) WhoIsIPPort(netaddr.IPPort) (netaddr.IP, bool) { return netaddr.IP{}, false }