feat(vpn): auto detection of IPv6 support

- `OPENVPN_IPV6` removed
- Affects OpenVPN
- Use the same mechanism for OpenVPN and Wireguard
- Check only once at program start since this is unlikely to change at runtime
- Log if IPv6 is supported
- Remove `IPv6` boolean from settings structs
- Move IPv6 detection as a method on NetLinker
This commit is contained in:
Quentin McGaw
2022-09-06 12:16:29 +00:00
parent 71c51a7455
commit 5ddd703f6a
45 changed files with 171 additions and 137 deletions

View File

@@ -55,6 +55,7 @@ func Test_New(t *testing.T) {
Mask: net.IPv4Mask(255, 255, 255, 255)},
},
FirewallMark: 100,
IPv6: ptr(false),
},
},
},

View File

@@ -1,34 +0,0 @@
package wireguard
import (
"fmt"
"github.com/qdm12/gluetun/internal/netlink"
)
func (w *Wireguard) isIPv6Supported() (supported bool, err error) {
links, err := w.netlink.LinkList()
if err != nil {
return false, fmt.Errorf("cannot list links: %w", err)
}
w.logger.Debug("Checking for IPv6 support...")
for _, link := range links {
linkName := link.Attrs().Name
routes, err := w.netlink.RouteList(link, netlink.FAMILY_V6)
if err != nil {
return false, fmt.Errorf("cannot list routes for link %s: %w", linkName, err)
}
if len(routes) == 0 {
w.logger.Debugf("Link %s has no IPv6 route", linkName)
continue
}
w.logger.Debugf("Link %s has IPv6 routes: %#v",
linkName, routes)
supported = true
}
return supported, nil
}

View File

@@ -16,7 +16,6 @@ import (
)
var (
ErrDetectIPv6 = errors.New("cannot detect IPv6 support")
ErrDetectKernel = errors.New("cannot detect Kernel support")
ErrCreateTun = errors.New("cannot create TUN device")
ErrAddLink = errors.New("cannot add Wireguard link")
@@ -36,12 +35,6 @@ var (
// See https://git.zx2c4.com/wireguard-go/tree/main.go
func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<- struct{}) {
doIPv6, err := w.isIPv6Supported()
if err != nil {
waitError <- fmt.Errorf("%w: %s", ErrDetectIPv6, err)
return
}
doKernel, err := w.netlink.IsWireguardSupported()
if err != nil {
waitError <- fmt.Errorf("%w: %s", ErrDetectKernel, err)
@@ -101,7 +94,7 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan<
return
}
if doIPv6 {
if *w.settings.IPv6 {
// requires net.ipv6.conf.all.disable_ipv6=0
err = w.addRoute(link, allIPv6(), w.settings.FirewallMark)
if err != nil {

View File

@@ -30,6 +30,9 @@ type Settings struct {
// RulePriority is the priority for the rule created with the
// FirewallMark.
RulePriority int
// IPv6 can bet set to true if IPv6 should be handled.
// It defaults to false if left unset.
IPv6 *bool
}
func (s *Settings) SetDefaults() {
@@ -47,6 +50,11 @@ func (s *Settings) SetDefaults() {
const defaultFirewallMark = 51820
s.FirewallMark = defaultFirewallMark
}
if s.IPv6 == nil {
ipv6 := false // this should be injected from host
s.IPv6 = &ipv6
}
}
var (
@@ -187,6 +195,12 @@ func (s Settings) ToLines(settings ToLinesSettings) (lines []string) {
}
lines = append(lines, fieldPrefix+"Endpoint: "+endpointStr)
ipv6Status := "disabled"
if *s.IPv6 {
ipv6Status = "enabled"
}
lines = append(lines, fieldPrefix+"IPv6: "+ipv6Status)
if s.FirewallMark != 0 {
lines = append(lines, fieldPrefix+"Firewall mark: "+fmt.Sprint(s.FirewallMark))
}

View File

@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/require"
)
func ptr[T any](v T) *T { return &v }
func Test_Settings_SetDefaults(t *testing.T) {
t.Parallel()
@@ -20,6 +22,7 @@ func Test_Settings_SetDefaults(t *testing.T) {
expected: Settings{
InterfaceName: "wg0",
FirewallMark: 51820,
IPv6: ptr(false),
},
},
"default endpoint port": {
@@ -35,6 +38,7 @@ func Test_Settings_SetDefaults(t *testing.T) {
IP: net.IPv4(1, 2, 3, 4),
Port: 51820,
},
IPv6: ptr(false),
},
},
"not empty settings": {
@@ -45,6 +49,7 @@ func Test_Settings_SetDefaults(t *testing.T) {
IP: net.IPv4(1, 2, 3, 4),
Port: 9999,
},
IPv6: ptr(true),
},
expected: Settings{
InterfaceName: "wg1",
@@ -53,6 +58,7 @@ func Test_Settings_SetDefaults(t *testing.T) {
IP: net.IPv4(1, 2, 3, 4),
Port: 9999,
},
IPv6: ptr(true),
},
},
}
@@ -282,11 +288,13 @@ func Test_Settings_String(t *testing.T) {
settings := Settings{
InterfaceName: "wg0",
IPv6: ptr(true),
}
const expected = `├── Interface name: wg0
├── Private key: not set
├── Pre shared key: not set
├── Endpoint: not set
├── IPv6: enabled
└── Addresses: not set`
s := settings.String()
assert.Equal(t, expected, s)
@@ -301,11 +309,15 @@ func Test_Settings_Lines(t *testing.T) {
lines []string
}{
"empty settings": {
settings: Settings{
IPv6: ptr(false),
},
lines: []string{
"├── Interface name: ",
"├── Private key: not set",
"├── Pre shared key: not set",
"├── Endpoint: not set",
"├── IPv6: disabled",
"└── Addresses: not set",
},
},
@@ -325,6 +337,7 @@ func Test_Settings_Lines(t *testing.T) {
{IP: net.IPv4(1, 1, 1, 1), Mask: net.CIDRMask(24, 32)},
{IP: net.IPv4(2, 2, 2, 2), Mask: net.CIDRMask(32, 32)},
},
IPv6: ptr(true),
},
lines: []string{
"├── Interface name: wg0",
@@ -332,6 +345,7 @@ func Test_Settings_Lines(t *testing.T) {
"├── PublicKey: public key",
"├── Pre shared key: set",
"├── Endpoint: 1.2.3.4:51820",
"├── IPv6: enabled",
"├── Firewall mark: 999",
"├── Rule priority: 888",
"└── Addresses:",
@@ -351,12 +365,14 @@ func Test_Settings_Lines(t *testing.T) {
{IP: net.IPv4(1, 1, 1, 1), Mask: net.CIDRMask(24, 32)},
{IP: net.IPv4(2, 2, 2, 2), Mask: net.CIDRMask(32, 32)},
},
IPv6: ptr(false),
},
lines: []string{
"- Interface name: wg0",
"- Private key: not set",
"- Pre shared key: not set",
"- Endpoint: not set",
"- IPv6: disabled",
"* Addresses:",
" - 1.1.1.1/24",
" * 2.2.2.2/32",