wgengine/router: add ip rules for unifi udm-pro

Fixes: #4038

Signed-off-by: Jason Barnett <J@sonBarnett.com>
pull/10828/head
Jason Barnett 10 months ago
parent ff1391a97e
commit c097c31135

@ -10,6 +10,7 @@ import (
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"tailscale.com/types/lazy" "tailscale.com/types/lazy"
"tailscale.com/util/lineread" "tailscale.com/util/lineread"
@ -31,6 +32,7 @@ const (
WDMyCloud = Distro("wdmycloud") WDMyCloud = Distro("wdmycloud")
Unraid = Distro("unraid") Unraid = Distro("unraid")
Alpine = Distro("alpine") Alpine = Distro("alpine")
UDMPro = Distro("udmpro")
) )
var distro lazy.SyncValue[Distro] var distro lazy.SyncValue[Distro]
@ -76,6 +78,9 @@ 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():
// UDM-Pro runs on debian
return UDMPro
case have("/etc/debian_version"): case have("/etc/debian_version"):
return Debian return Debian
case have("/etc/arch-release"): case have("/etc/arch-release"):
@ -147,3 +152,25 @@ func DSMVersion() int {
return v return v
}) })
} }
func isUDMPro() bool {
if exists, err := fileContainsString("/etc/board.info", "UDMPRO"); err == nil && exists {
return true
}
if exists, err := fileContainsString("/etc/board.info", "Dream Machine PRO"); err == nil && exists {
return true
}
if exists, err := fileContainsString("/sys/firmware/devicetree/base/soc/board-cfg/id", "udm pro"); err == nil && exists {
return true
}
return false
}
func fileContainsString(filePath, searchString string) (bool, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return false, err
}
return strings.Contains(string(data), searchString), nil
}

@ -1031,6 +1031,10 @@ func mustRouteTable(num int) RouteTable {
var ( var (
mainRouteTable = newRouteTable("main", 254) mainRouteTable = newRouteTable("main", 254)
defaultRouteTable = newRouteTable("default", 253) defaultRouteTable = newRouteTable("default", 253)
// Port 9 - WAN
udmProRouteTable1 = newRouteTable("201", 201)
// Port 10 - SFP+ WAN 2
udmProRouteTable2 = newRouteTable("202", 202)
// tailscaleRouteTable is the routing table number for Tailscale // tailscaleRouteTable is the routing table number for Tailscale
// network routes. See addIPRules for the detailed policy routing // network routes. See addIPRules for the detailed policy routing
@ -1051,7 +1055,7 @@ var (
tailscaleRouteTable = newRouteTable("tailscale", 52) tailscaleRouteTable = newRouteTable("tailscale", 52)
) )
// ipRules are the policy routing rules that Tailscale uses. // _ipRules are the policy routing rules that Tailscale uses.
// 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.
// //
@ -1066,7 +1070,7 @@ var (
// and 'ip rule' implementations (including busybox), don't support // and 'ip rule' implementations (including busybox), don't support
// checking for the lack of a fwmark, only the presence. The technique // checking for the lack of a fwmark, only the presence. The technique
// below works even on very old kernels. // below works even on very old kernels.
var ipRules = []netlink.Rule{ var _ipRules = []netlink.Rule{
// Packets from us, tagged with our fwmark, first try the kernel's // Packets from us, tagged with our fwmark, first try the kernel's
// main routing table. // main routing table.
{ {
@ -1102,6 +1106,17 @@ var ipRules = []netlink.Rule{
// usual rules (pref 32766 and 32767, ie. main and default). // usual rules (pref 32766 and 32767, ie. main and default).
} }
var addedUDMProIPRules = false
func ipRules() []netlink.Rule {
// lazy add UDM Pro rules
if distro.Get() == distro.UDMPro {
addUDMProIpRules()
}
return _ipRules
}
// justAddIPRules adds policy routing rule without deleting any first. // justAddIPRules adds policy routing rule without deleting any first.
func (r *linuxRouter) justAddIPRules() error { func (r *linuxRouter) justAddIPRules() error {
if !r.ipRuleAvailable { if !r.ipRuleAvailable {
@ -1113,7 +1128,7 @@ func (r *linuxRouter) justAddIPRules() error {
var errAcc error var errAcc error
for _, family := range r.addrFamilies() { for _, family := range r.addrFamilies() {
for _, ru := range ipRules { for _, ru := range ipRules() {
// Note: r is a value type here; safe to mutate it. // Note: r is a value type here; safe to mutate it.
ru.Family = family.netlinkInt() ru.Family = family.netlinkInt()
if ru.Mark != 0 { if ru.Mark != 0 {
@ -1142,7 +1157,7 @@ func (r *linuxRouter) addIPRulesWithIPCommand() error {
rg := newRunGroup(nil, r.cmd) rg := newRunGroup(nil, r.cmd)
for _, family := range r.addrFamilies() { for _, family := range r.addrFamilies() {
for _, rule := range ipRules { for _, rule := range ipRules() {
args := []string{ args := []string{
"ip", family.dashArg(), "ip", family.dashArg(),
"rule", "add", "rule", "add",
@ -1190,7 +1205,7 @@ func (r *linuxRouter) delIPRules() error {
} }
var errAcc error var errAcc error
for _, family := range r.addrFamilies() { for _, family := range r.addrFamilies() {
for _, ru := range ipRules { for _, ru := range ipRules() {
// Note: r is a value type here; safe to mutate it. // Note: r is a value type here; safe to mutate it.
// When deleting rules, we want to be a bit specific (mention which // When deleting rules, we want to be a bit specific (mention which
// table we were routing to) but not *too* specific (fwmarks, etc). // table we were routing to) but not *too* specific (fwmarks, etc).
@ -1233,7 +1248,7 @@ func (r *linuxRouter) delIPRulesWithIPCommand() error {
// That leaves us some flexibility to change these values in later // That leaves us some flexibility to change these values in later
// versions without having ongoing hacks for every possible // versions without having ongoing hacks for every possible
// combination. // combination.
for _, rule := range ipRules { for _, rule := range ipRules() {
args := []string{ args := []string{
"ip", family.dashArg(), "ip", family.dashArg(),
"rule", "del", "rule", "del",
@ -1395,3 +1410,22 @@ func nlAddrOfPrefix(p netip.Prefix) *netlink.Addr {
IPNet: netipx.PrefixIPNet(p), IPNet: netipx.PrefixIPNet(p),
} }
} }
// Adds necessary ip rules for UDM-Pro. See issue 4038.
func addUDMProIpRules() {
if addedUDMProIPRules {
return
}
_ipRules = append(_ipRules, netlink.Rule{
Priority: 21,
Mark: linuxfw.TailscaleBypassMarkNum,
Table: udmProRouteTable1.Num,
}, netlink.Rule{
Priority: 22,
Mark: linuxfw.TailscaleBypassMarkNum,
Table: udmProRouteTable2.Num,
})
addedUDMProIPRules = true
}

@ -19,6 +19,7 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
ts_netlink "github.com/tailscale/netlink"
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"go4.org/netipx" "go4.org/netipx"
@ -1132,3 +1133,38 @@ func adjustFwmask(t *testing.T, s string) string {
return fwmaskAdjustRe.ReplaceAllString(s, "$1") return fwmaskAdjustRe.ReplaceAllString(s, "$1")
} }
func TestAddUDMProIpRules(t *testing.T) {
originalIpRules := make([]ts_netlink.Rule, len(_ipRules))
copy(originalIpRules, _ipRules)
addUDMProIpRules()
expectedRules := []ts_netlink.Rule{
{
Priority: 21,
Mark: linuxfw.TailscaleBypassMarkNum,
Table: udmProRouteTable1.Num,
},
{
Priority: 22,
Mark: linuxfw.TailscaleBypassMarkNum,
Table: udmProRouteTable2.Num,
},
}
for _, expected := range expectedRules {
found := false
for _, actual := range ipRules() {
if reflect.DeepEqual(actual, expected) {
found = true
break
}
}
if !found {
t.Errorf("Expected rule %+v; not found in ipRules", expected)
}
}
_ipRules = originalIpRules
}

Loading…
Cancel
Save