wgengine/router: refactor udm-pro into broader ubnt support

Fixes #14453

Signed-off-by: Jason Barnett <J@sonBarnett.com>
This commit is contained in:
Jason Barnett 2024-12-22 13:18:40 -07:00 committed by Adrian Dewhurst
parent e3bcb2ec83
commit 17b881538a
3 changed files with 17 additions and 56 deletions

View File

@ -9,7 +9,6 @@
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"tailscale.com/types/lazy" "tailscale.com/types/lazy"
"tailscale.com/util/lineiter" "tailscale.com/util/lineiter"
@ -31,7 +30,7 @@
WDMyCloud = Distro("wdmycloud") WDMyCloud = Distro("wdmycloud")
Unraid = Distro("unraid") Unraid = Distro("unraid")
Alpine = Distro("alpine") Alpine = Distro("alpine")
UDMPro = Distro("udmpro") UBNT = Distro("ubnt") // Ubiquiti Networks
) )
var distro lazy.SyncValue[Distro] var distro lazy.SyncValue[Distro]
@ -77,9 +76,12 @@ func linuxDistro() Distro {
case have("/usr/local/bin/freenas-debug"): case have("/usr/local/bin/freenas-debug"):
// TrueNAS Scale runs on debian // TrueNAS Scale runs on debian
return TrueNAS return TrueNAS
case isUDMPro(): case have("/usr/bin/ubnt-device-info"):
// UDM-Pro runs on debian // UBNT runs on Debian-based systems. This MUST be checked before Debian.
return UDMPro //
// Currently supported product families:
// - UDM (UniFi Dream Machine, UDM-Pro)
return UBNT
case have("/etc/debian_version"): case have("/etc/debian_version"):
return Debian return Debian
case have("/etc/arch-release"): case have("/etc/arch-release"):
@ -152,44 +154,3 @@ func DSMVersion() int {
return 0 return 0
}) })
} }
// isUDMPro checks a couple of files known to exist on a UDM-Pro and returns
// true if the expected content exists in the files.
func isUDMPro() bool {
// This is a performance guardrail against trying to load both
// /etc/board.info and /sys/firmware/devicetree/base/soc/board-cfg/id when
// not running on Debian so we don't make unnecessary calls in situations
// where we definitely are NOT on a UDM Pro. In other words, the have() call
// is much cheaper than the two os.ReadFile() in fileContainsAnyString().
// That said, on Debian systems we will still be making the two
// os.ReadFile() in fileContainsAnyString().
if !have("/etc/debian_version") {
return false
}
if exists, err := fileContainsAnyString("/etc/board.info", "UDMPRO", "Dream Machine PRO"); err == nil && exists {
return true
}
if exists, err := fileContainsAnyString("/sys/firmware/devicetree/base/soc/board-cfg/id", "udm pro"); err == nil && exists {
return true
}
return false
}
// fileContainsAnyString is used to determine if one or more of the provided
// strings exists in a file. This is not efficient for larger files. If you want
// to use this function to parse large files, please refactor to use
// `io.LimitedReader`.
func fileContainsAnyString(filePath string, searchStrings ...string) (bool, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return false, err
}
content := string(data)
for _, searchString := range searchStrings {
if strings.Contains(content, searchString) {
return true, nil
}
}
return false, nil
}

View File

@ -1184,7 +1184,7 @@ func mustRouteTable(num int) RouteTable {
) )
// baseIPRules are the policy routing rules that Tailscale uses, when not // baseIPRules are the policy routing rules that Tailscale uses, when not
// running on a UDM-Pro. // running on a UBNT device.
// //
// The priority is the value represented here added to r.ipPolicyPrefBase, // The priority is the value represented here added to r.ipPolicyPrefBase,
// which is usually 5200. // which is usually 5200.
@ -1236,15 +1236,15 @@ func mustRouteTable(num int) RouteTable {
// usual rules (pref 32766 and 32767, ie. main and default). // usual rules (pref 32766 and 32767, ie. main and default).
} }
// udmProIPRules are the policy routing rules that Tailscale uses, when running // ubntIPRules are the policy routing rules that Tailscale uses, when running
// on a UDM-Pro. // on a UBNT device.
// //
// The priority is the value represented here added to // The priority is the value represented here added to
// r.ipPolicyPrefBase, which is usually 5200. // r.ipPolicyPrefBase, which is usually 5200.
// //
// This represents an experiment that will be used to gather more information. // This represents an experiment that will be used to gather more information.
// If this goes well, Tailscale may opt to use this for all of Linux. // If this goes well, Tailscale may opt to use this for all of Linux.
var udmProIPRules = []netlink.Rule{ var ubntIPRules = []netlink.Rule{
// non-fwmark packets fall through to the usual rules (pref 32766 and 32767, // non-fwmark packets fall through to the usual rules (pref 32766 and 32767,
// ie. main and default). // ie. main and default).
{ {
@ -1256,10 +1256,10 @@ func mustRouteTable(num int) RouteTable {
} }
// ipRules returns the appropriate list of ip rules to be used by Tailscale. See // ipRules returns the appropriate list of ip rules to be used by Tailscale. See
// comments on baseIPRules and udmProIPRules for more details. // comments on baseIPRules and ubntIPRules for more details.
func ipRules() []netlink.Rule { func ipRules() []netlink.Rule {
if getDistroFunc() == distro.UDMPro { if getDistroFunc() == distro.UBNT {
return udmProIPRules return ubntIPRules
} }
return baseIPRules return baseIPRules
} }

View File

@ -1233,14 +1233,14 @@ func adjustFwmask(t *testing.T, s string) string {
return fwmaskAdjustRe.ReplaceAllString(s, "$1") return fwmaskAdjustRe.ReplaceAllString(s, "$1")
} }
func TestIPRulesForUDMPro(t *testing.T) { func TestIPRulesForUBNT(t *testing.T) {
// Override the global getDistroFunc // Override the global getDistroFunc
getDistroFunc = func() distro.Distro { getDistroFunc = func() distro.Distro {
return distro.UDMPro return distro.UBNT
} }
defer func() { getDistroFunc = distro.Get }() // Restore original after the test defer func() { getDistroFunc = distro.Get }() // Restore original after the test
expected := udmProIPRules expected := ubntIPRules
actual := ipRules() actual := ipRules()
if len(expected) != len(actual) { if len(expected) != len(actual) {