mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
wgengine/{monitor,router}: restore Linux ip rules when systemd deletes them
Thanks. Fixes #1591 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
0aa77ba80f
commit
e4fecfe31d
@@ -13,12 +13,15 @@ import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/go-multierror/multierror"
|
||||
"golang.org/x/time/rate"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/types/preftype"
|
||||
"tailscale.com/version/distro"
|
||||
@@ -95,15 +98,22 @@ type netfilterRunner interface {
|
||||
}
|
||||
|
||||
type linuxRouter struct {
|
||||
closed syncs.AtomicBool
|
||||
logf func(fmt string, args ...interface{})
|
||||
tunname string
|
||||
linkMon *monitor.Mon
|
||||
unregLinkMon func()
|
||||
addrs map[netaddr.IPPrefix]bool
|
||||
routes map[netaddr.IPPrefix]bool
|
||||
localRoutes map[netaddr.IPPrefix]bool
|
||||
snatSubnetRoutes bool
|
||||
netfilterMode preftype.NetfilterMode
|
||||
|
||||
// ruleRestorePending is whether a timer has been started to
|
||||
// restore deleted ip rules.
|
||||
ruleRestorePending syncs.AtomicBool
|
||||
ipRuleFixLimiter *rate.Limiter
|
||||
|
||||
// Various feature checks for the network stack.
|
||||
ipRuleAvailable bool
|
||||
v6Available bool
|
||||
@@ -151,7 +161,7 @@ func newUserspaceRouter(logf logger.Logf, tunDev tun.Device, linkMon *monitor.Mo
|
||||
func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, linkMon *monitor.Mon, netfilter4, netfilter6 netfilterRunner, cmd commandRunner, supportsV6, supportsV6NAT bool) (Router, error) {
|
||||
ipRuleAvailable := (cmd.run("ip", "rule") == nil)
|
||||
|
||||
return &linuxRouter{
|
||||
r := &linuxRouter{
|
||||
logf: logf,
|
||||
tunname: tunname,
|
||||
netfilterMode: netfilterOff,
|
||||
@@ -164,10 +174,52 @@ func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, linkMon *monit
|
||||
ipt4: netfilter4,
|
||||
ipt6: netfilter6,
|
||||
cmd: cmd,
|
||||
}, nil
|
||||
|
||||
ipRuleFixLimiter: rate.NewLimiter(rate.Every(5*time.Second), 10),
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// onIPRuleDeleted is the callback from the link monitor for when an IP policy
|
||||
// rule is deleted. See Issue 1591.
|
||||
//
|
||||
// If an ip rule is deleted (with pref number 52xx, as Tailscale sets), then
|
||||
// set a timer to restore our rules, in case they were deleted. The timer lets
|
||||
// us do one fixup in response to a batch of rule deletes. It also lets us
|
||||
// delay arbitrarily to prevent a high-speed fight over the rule between
|
||||
// competiting processes. (Although empirically, systemd doesn't fight us
|
||||
// like that... yet.)
|
||||
//
|
||||
// Note that we don't care about the table number. We don't strictly even care
|
||||
// about the priority number. We could just do this in response to any netlink
|
||||
// change. Filtering by known priority ranges cuts back on some logspam.
|
||||
func (r *linuxRouter) onIPRuleDeleted(table uint8, priority uint32) {
|
||||
if priority < 5200 || priority >= 5300 {
|
||||
// Not our rule.
|
||||
return
|
||||
}
|
||||
if !r.ruleRestorePending.Swap(true) {
|
||||
// Another timer is already pending.
|
||||
return
|
||||
}
|
||||
rr := r.ipRuleFixLimiter.Reserve()
|
||||
if !rr.OK() {
|
||||
r.ruleRestorePending.Swap(false)
|
||||
return
|
||||
}
|
||||
time.AfterFunc(rr.Delay()+250*time.Millisecond, func() {
|
||||
if r.ruleRestorePending.Swap(false) && !r.closed.Get() {
|
||||
r.logf("somebody (likely systemd-networkd) deleted ip rules; restoring Tailscale's")
|
||||
r.justAddIPRules()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (r *linuxRouter) Up() error {
|
||||
if r.unregLinkMon == nil && r.linkMon != nil {
|
||||
r.unregLinkMon = r.linkMon.RegisterRuleDeleteCallback(r.onIPRuleDeleted)
|
||||
}
|
||||
if err := r.delLegacyNetfilter(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -185,6 +237,10 @@ func (r *linuxRouter) Up() error {
|
||||
}
|
||||
|
||||
func (r *linuxRouter) Close() error {
|
||||
r.closed.Set(true)
|
||||
if r.unregLinkMon != nil {
|
||||
r.unregLinkMon()
|
||||
}
|
||||
if err := r.downInterface(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -565,6 +621,15 @@ func (r *linuxRouter) addIPRules() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.justAddIPRules()
|
||||
}
|
||||
|
||||
// justAddIPRules adds policy routing rule without deleting any first.
|
||||
func (r *linuxRouter) justAddIPRules() error {
|
||||
if !r.ipRuleAvailable {
|
||||
return nil
|
||||
}
|
||||
|
||||
rg := newRunGroup(nil, r.cmd)
|
||||
|
||||
for _, family := range r.iprouteFamilies() {
|
||||
|
Reference in New Issue
Block a user