From 11aeee2d7649f25ef91c5632cdf733e36e4e238d Mon Sep 17 00:00:00 2001
From: Matt Layher
Date: Wed, 15 May 2019 13:21:13 -0400
Subject: [PATCH] internal/wglinux: gracefully degrade if kernel implementation
not available
---
internal/wglinux/client_linux.go | 19 +++++++++++++------
internal/wglinux/client_linux_test.go | 26 ++++++++++++++++++++++++--
os_linux.go | 17 ++++++++++++-----
3 files changed, 49 insertions(+), 13 deletions(-)
diff --git a/internal/wglinux/client_linux.go b/internal/wglinux/client_linux.go
index ebbc534..9e7cf57 100644
--- a/internal/wglinux/client_linux.go
+++ b/internal/wglinux/client_linux.go
@@ -26,22 +26,29 @@ type Client struct {
interfaces func() ([]string, error)
}
-// New creates a new Client.
-func New() (*Client, error) {
+// New creates a new Client and returns whether or not the generic netlink
+// interface is available.
+func New() (*Client, bool, error) {
c, err := genetlink.Dial(nil)
if err != nil {
- return nil, err
+ return nil, false, err
}
return initClient(c)
}
// initClient is the internal Client constructor used in some tests.
-func initClient(c *genetlink.Conn) (*Client, error) {
+func initClient(c *genetlink.Conn) (*Client, bool, error) {
f, err := c.GetFamily(wgh.GenlName)
if err != nil {
_ = c.Close()
- return nil, err
+
+ if netlink.IsNotExist(err) {
+ // The generic netlink interface is not available.
+ return nil, false, nil
+ }
+
+ return nil, false, err
}
return &Client{
@@ -50,7 +57,7 @@ func initClient(c *genetlink.Conn) (*Client, error) {
// By default, gather only WireGuard interfaces using rtnetlink.
interfaces: rtnlInterfaces,
- }, nil
+ }, true, nil
}
// Close implements wginternal.Client.
diff --git a/internal/wglinux/client_linux_test.go b/internal/wglinux/client_linux_test.go
index d8cd223..533f12b 100644
--- a/internal/wglinux/client_linux_test.go
+++ b/internal/wglinux/client_linux_test.go
@@ -131,10 +131,14 @@ func TestLinuxClientIsPermission(t *testing.T) {
t.Skip("skipping, test must be run without elevated privileges")
}
- c, err := New()
+ c, ok, err := New()
if err != nil {
t.Fatalf("failed to create Client: %v", err)
}
+ if !ok {
+ t.Skip("skipping, the WireGuard generic netlink API is not available")
+ }
+
defer c.Close()
// Check for permission denied as unprivileged user.
@@ -143,6 +147,21 @@ func TestLinuxClientIsPermission(t *testing.T) {
}
}
+func Test_initClientNotExist(t *testing.T) {
+ conn := genltest.Dial(func(_ genetlink.Message, _ netlink.Message) ([]genetlink.Message, error) {
+ // Simulate genetlink family not found.
+ return nil, genltest.Error(int(unix.ENOENT))
+ })
+
+ _, ok, err := initClient(conn)
+ if err != nil {
+ t.Fatalf("failed to open Client: %v", err)
+ }
+ if ok {
+ t.Fatal("the generic netlink API should not be available from genltest")
+ }
+}
+
func Test_parseRTNLInterfaces(t *testing.T) {
// marshalAttrs creates packed netlink attributes with a prepended ifinfomsg
// structure, as returned by rtnetlink.
@@ -266,10 +285,13 @@ func testClient(t *testing.T, fn genltest.Func) *Client {
conn := genltest.Dial(genltest.ServeFamily(family, fn))
- c, err := initClient(conn)
+ c, ok, err := initClient(conn)
if err != nil {
t.Fatalf("failed to open Client: %v", err)
}
+ if !ok {
+ t.Fatal("the generic netlink API was not available from genltest")
+ }
c.interfaces = func() ([]string, error) {
return []string{okName}, nil
diff --git a/os_linux.go b/os_linux.go
index 1c36c51..a6ab3d9 100644
--- a/os_linux.go
+++ b/os_linux.go
@@ -10,19 +10,26 @@ import (
// newClients configures wginternal.Clients for Linux systems.
func newClients() ([]wginternal.Client, error) {
- // Linux has an in-kernel WireGuard implementation.
- nlc, err := wglinux.New()
+ var clients []wginternal.Client
+
+ // Linux has an in-kernel WireGuard implementation. Determine if it is
+ // available and make use of it if so.
+ kc, ok, err := wglinux.New()
if err != nil {
return nil, err
}
+ if ok {
+ clients = append(clients, kc)
+ }
// Although it isn't recommended to use userspace implementations on Linux,
// it can be used. We make use of it in integration tests as well.
- cfgc, err := wguser.New()
+ uc, err := wguser.New()
if err != nil {
return nil, err
}
- // Netlink devices seem to appear first in wg(8).
- return []wginternal.Client{nlc, cfgc}, nil
+ // Kernel devices seem to appear first in wg(8).
+ clients = append(clients, uc)
+ return clients, nil
}