wgengine/router: add ip rules for unifi udm-pro
Fixes: #4038 Signed-off-by: Jason Barnett <J@sonBarnett.com>
This commit is contained in:
parent
ff1391a97e
commit
c097c31135
|
@ -10,6 +10,7 @@ import (
|
|||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/types/lazy"
|
||||
"tailscale.com/util/lineread"
|
||||
|
@ -31,6 +32,7 @@ const (
|
|||
WDMyCloud = Distro("wdmycloud")
|
||||
Unraid = Distro("unraid")
|
||||
Alpine = Distro("alpine")
|
||||
UDMPro = Distro("udmpro")
|
||||
)
|
||||
|
||||
var distro lazy.SyncValue[Distro]
|
||||
|
@ -76,6 +78,9 @@ func linuxDistro() Distro {
|
|||
case have("/usr/local/bin/freenas-debug"):
|
||||
// TrueNAS Scale runs on debian
|
||||
return TrueNAS
|
||||
case isUDMPro():
|
||||
// UDM-Pro runs on debian
|
||||
return UDMPro
|
||||
case have("/etc/debian_version"):
|
||||
return Debian
|
||||
case have("/etc/arch-release"):
|
||||
|
@ -147,3 +152,25 @@ func DSMVersion() int {
|
|||
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 (
|
||||
mainRouteTable = newRouteTable("main", 254)
|
||||
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
|
||||
// network routes. See addIPRules for the detailed policy routing
|
||||
|
@ -1051,7 +1055,7 @@ var (
|
|||
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,
|
||||
// which is usually 5200.
|
||||
//
|
||||
|
@ -1066,7 +1070,7 @@ var (
|
|||
// and 'ip rule' implementations (including busybox), don't support
|
||||
// checking for the lack of a fwmark, only the presence. The technique
|
||||
// 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
|
||||
// main routing table.
|
||||
{
|
||||
|
@ -1102,6 +1106,17 @@ var ipRules = []netlink.Rule{
|
|||
// 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.
|
||||
func (r *linuxRouter) justAddIPRules() error {
|
||||
if !r.ipRuleAvailable {
|
||||
|
@ -1113,7 +1128,7 @@ func (r *linuxRouter) justAddIPRules() error {
|
|||
var errAcc error
|
||||
for _, family := range r.addrFamilies() {
|
||||
|
||||
for _, ru := range ipRules {
|
||||
for _, ru := range ipRules() {
|
||||
// Note: r is a value type here; safe to mutate it.
|
||||
ru.Family = family.netlinkInt()
|
||||
if ru.Mark != 0 {
|
||||
|
@ -1142,7 +1157,7 @@ func (r *linuxRouter) addIPRulesWithIPCommand() error {
|
|||
rg := newRunGroup(nil, r.cmd)
|
||||
|
||||
for _, family := range r.addrFamilies() {
|
||||
for _, rule := range ipRules {
|
||||
for _, rule := range ipRules() {
|
||||
args := []string{
|
||||
"ip", family.dashArg(),
|
||||
"rule", "add",
|
||||
|
@ -1190,7 +1205,7 @@ func (r *linuxRouter) delIPRules() error {
|
|||
}
|
||||
var errAcc error
|
||||
for _, family := range r.addrFamilies() {
|
||||
for _, ru := range ipRules {
|
||||
for _, ru := range ipRules() {
|
||||
// Note: r is a value type here; safe to mutate it.
|
||||
// When deleting rules, we want to be a bit specific (mention which
|
||||
// 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
|
||||
// versions without having ongoing hacks for every possible
|
||||
// combination.
|
||||
for _, rule := range ipRules {
|
||||
for _, rule := range ipRules() {
|
||||
args := []string{
|
||||
"ip", family.dashArg(),
|
||||
"rule", "del",
|
||||
|
@ -1395,3 +1410,22 @@ func nlAddrOfPrefix(p netip.Prefix) *netlink.Addr {
|
|||
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"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
ts_netlink "github.com/tailscale/netlink"
|
||||
"github.com/tailscale/wireguard-go/tun"
|
||||
"github.com/vishvananda/netlink"
|
||||
"go4.org/netipx"
|
||||
|
@ -1132,3 +1133,38 @@ func adjustFwmask(t *testing.T, s string) string {
|
|||
|
||||
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…
Reference in New Issue