mirror of
https://github.com/tailscale/tailscale.git
synced 2026-10-23 03:37:19 +09:00
net/netutil: check actual IP forwarding sysctls on FreeBSD
CheckIPForwarding unconditionally returned a "not currently officially supported" warning on FreeBSD without ever reading the forwarding sysctls. Subnet routers on FreeBSD therefore got a spurious "IP forwarding is disabled" health warning and admin console banner even with net.inet.ip.forwarding=1 and net.inet6.ip6.forwarding=1 set. Read the sysctls instead, and only warn for the protocols actually required by the advertised routes. FreeBSD has no per-interface forwarding knob, so only the global sysctls are checked. dragonfly, netbsd and openbsd keep the previous unsupported warning. Updates #5573 Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
This commit is contained in:
1 parent
650bb0546a
commit
58f28a1927
2 files changed
+121
-1
No files matched your search
@@ -60,7 +60,12 @@ func protocolsRequiredForForwarding(routes []netip.Prefix, state *netmon.State)
|
||||
func CheckIPForwarding(routes []netip.Prefix, state *netmon.State) (warn, err error) {
|
||||
if runtime.GOOS != "linux" {
|
||||
switch runtime.GOOS {
|
||||
case "dragonfly", "freebsd", "netbsd", "openbsd":
|
||||
case "freebsd":
|
||||
if state == nil {
|
||||
return nil, fmt.Errorf("Couldn't check system's IP forwarding configuration; no link state")
|
||||
}
|
||||
return checkIPForwardingFreeBSD(routes, state)
|
||||
case "dragonfly", "netbsd", "openbsd":
|
||||
return fmt.Errorf("Subnet routing and exit nodes only work with additional manual configuration on %v, and is not currently officially supported.", runtime.GOOS), nil
|
||||
case "illumos", "solaris":
|
||||
_, err := ipForwardingEnabledSunOS(ipv4, "")
|
||||
@@ -235,6 +240,61 @@ func ipForwardingEnabledLinux(p protocol, iface string) (bool, error) {
|
||||
return on, nil
|
||||
}
|
||||
|
||||
// checkIPForwardingFreeBSD reports whether IP forwarding is enabled for the
|
||||
// protocols required by routes.
|
||||
//
|
||||
// Unlike Linux, FreeBSD has no per-interface forwarding knob: net.inet.ip.forwarding
|
||||
// and net.inet6.ip6.forwarding are global, so we only check those.
|
||||
func checkIPForwardingFreeBSD(routes []netip.Prefix, state *netmon.State) (warn, err error) {
|
||||
const kbLink = "\nSee https://tailscale.com/s/ip-forwarding"
|
||||
wantV4, wantV6 := protocolsRequiredForForwarding(routes, state)
|
||||
if !wantV4 && !wantV6 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
if wantV4 {
|
||||
on, err := ipForwardingEnabledFreeBSD(ipv4)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't check system's IP forwarding configuration, subnet routing/exit nodes may not work: %w%s", err, kbLink)
|
||||
}
|
||||
if !on {
|
||||
warnings = append(warnings, "IPv4 forwarding is disabled.")
|
||||
}
|
||||
}
|
||||
if wantV6 {
|
||||
on, err := ipForwardingEnabledFreeBSD(ipv6)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't check system's IP forwarding configuration, subnet routing/exit nodes may not work: %w%s", err, kbLink)
|
||||
}
|
||||
if !on {
|
||||
warnings = append(warnings, "IPv6 forwarding is disabled.")
|
||||
}
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
return fmt.Errorf("%s\nSubnet routes and exit nodes may not work correctly.%s", strings.Join(warnings, "\n"), kbLink), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ipForwardingEnabledFreeBSD reports whether IP forwarding is enabled globally
|
||||
// for the given protocol.
|
||||
func ipForwardingEnabledFreeBSD(p protocol) (bool, error) {
|
||||
k := "net.inet.ip.forwarding"
|
||||
if p == ipv6 {
|
||||
k = "net.inet6.ip6.forwarding"
|
||||
}
|
||||
bs, err := exec.Command("sysctl", "-n", k).Output()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't check sysctl %s: %w", k, err)
|
||||
}
|
||||
val, err := strconv.ParseInt(string(bytes.TrimSpace(bs)), 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("couldn't parse %s: %w", k, err)
|
||||
}
|
||||
return val == 1, nil
|
||||
}
|
||||
|
||||
func ipForwardingEnabledSunOS(p protocol, iface string) (bool, error) {
|
||||
var proto string
|
||||
if p == ipv4 {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/net/netmon"
|
||||
)
|
||||
|
||||
func TestProtocolsRequiredForForwarding(t *testing.T) {
|
||||
state := &netmon.State{
|
||||
InterfaceIPs: map[string][]netip.Prefix{
|
||||
"em0": {netip.MustParsePrefix("192.168.1.5/24")},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
routes []netip.Prefix
|
||||
wantV4 bool
|
||||
wantV6 bool
|
||||
}{
|
||||
{"none", nil, false, false},
|
||||
{"v4only", []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")}, true, false},
|
||||
{"v6only", []netip.Prefix{netip.MustParsePrefix("2000::/3")}, false, true},
|
||||
{
|
||||
"both",
|
||||
[]netip.Prefix{netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("2000::/3")},
|
||||
true, true,
|
||||
},
|
||||
{
|
||||
// Advertising a route to one of our own local IPs doesn't
|
||||
// require forwarding, so it must not trigger a warning.
|
||||
"local single IP",
|
||||
[]netip.Prefix{netip.MustParsePrefix("192.168.1.5/32")},
|
||||
false, false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v4, v6 := protocolsRequiredForForwarding(tt.routes, state)
|
||||
if v4 != tt.wantV4 || v6 != tt.wantV6 {
|
||||
t.Errorf("got v4=%v v6=%v, want v4=%v v6=%v", v4, v6, tt.wantV4, tt.wantV6)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckIPForwardingFreeBSDNoRoutes verifies we don't shell out to sysctl
|
||||
// (and so can't warn) when there are no routes needing forwarding. This is the
|
||||
// path taken by a FreeBSD node that isn't a subnet router.
|
||||
func TestCheckIPForwardingFreeBSDNoRoutes(t *testing.T) {
|
||||
state := &netmon.State{}
|
||||
warn, err := checkIPForwardingFreeBSD(nil, state)
|
||||
if warn != nil || err != nil {
|
||||
t.Fatalf("got warn=%v err=%v, want both nil", warn, err)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user