mirror of
https://github.com/qdm12/gluetun.git
synced 2026-06-23 20:37:35 +02:00
chore!(amneziawg): refactor to be separate from wireguard
- amneziawg is now a VPN protocol and no longer a Wireguard implementation - Use it with VPN_TYPE=amneziawg - document AMNEZIAWG_* options in Dockerfile - document amneziawg support in readme - separate amneziawg settings and code from wireguard - re-use code from wireguard whenever possible
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package amneziawg
|
||||
|
||||
type Amneziawg struct {
|
||||
logger Logger
|
||||
settings Settings
|
||||
netlink NetLinker
|
||||
}
|
||||
|
||||
func New(settings Settings, netlink NetLinker,
|
||||
logger Logger,
|
||||
) (a *Amneziawg, err error) {
|
||||
settings.SetDefaults()
|
||||
if err := settings.Check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Amneziawg{
|
||||
logger: logger,
|
||||
settings: settings,
|
||||
netlink: netlink,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/wireguard"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
)
|
||||
|
||||
func Test_New(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const validKeyString = "oMNSf/zJ0pt1ciy+qIRk8Rlyfs9accwuRLnKd85Yl1Q="
|
||||
logger := NewMockLogger(nil)
|
||||
netLinker := NewMockNetLinker(nil)
|
||||
|
||||
testCases := map[string]struct {
|
||||
settings Settings
|
||||
amneziawg *Amneziawg
|
||||
err error
|
||||
}{
|
||||
"bad_settings": {
|
||||
settings: Settings{
|
||||
Wireguard: wireguard.Settings{
|
||||
PrivateKey: "",
|
||||
},
|
||||
},
|
||||
err: wireguard.ErrPrivateKeyMissing,
|
||||
},
|
||||
"minimal valid settings": {
|
||||
settings: Settings{
|
||||
Wireguard: wireguard.Settings{
|
||||
PrivateKey: validKeyString,
|
||||
PublicKey: validKeyString,
|
||||
Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 0),
|
||||
Addresses: []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{5, 6, 7, 8}), 32),
|
||||
},
|
||||
FirewallMark: 100,
|
||||
},
|
||||
},
|
||||
amneziawg: &Amneziawg{
|
||||
logger: logger,
|
||||
netlink: netLinker,
|
||||
settings: Settings{
|
||||
Wireguard: wireguard.Settings{
|
||||
InterfaceName: "wg0",
|
||||
PrivateKey: validKeyString,
|
||||
PublicKey: validKeyString,
|
||||
Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 51820),
|
||||
Addresses: []netip.Prefix{
|
||||
netip.PrefixFrom(netip.AddrFrom4([4]byte{5, 6, 7, 8}), 32),
|
||||
},
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"),
|
||||
},
|
||||
FirewallMark: 100,
|
||||
MTU: device.DefaultMTU,
|
||||
IPv6: ptrTo(false),
|
||||
Implementation: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
wireguard, err := New(testCase.settings, netLinker, logger)
|
||||
|
||||
if testCase.err != nil {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, testCase.amneziawg, wireguard)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package amneziawg
|
||||
|
||||
func ptrTo[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package amneziawg
|
||||
|
||||
//go:generate mockgen -destination=log_mock_test.go -package amneziawg . Logger
|
||||
|
||||
type Logger interface {
|
||||
Debug(s string)
|
||||
Debugf(format string, args ...interface{})
|
||||
Info(s string)
|
||||
Error(s string)
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/qdm12/gluetun/internal/amneziawg (interfaces: Logger)
|
||||
|
||||
// Package amneziawg is a generated GoMock package.
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockLogger is a mock of Logger interface.
|
||||
type MockLogger struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockLoggerMockRecorder
|
||||
}
|
||||
|
||||
// MockLoggerMockRecorder is the mock recorder for MockLogger.
|
||||
type MockLoggerMockRecorder struct {
|
||||
mock *MockLogger
|
||||
}
|
||||
|
||||
// NewMockLogger creates a new mock instance.
|
||||
func NewMockLogger(ctrl *gomock.Controller) *MockLogger {
|
||||
mock := &MockLogger{ctrl: ctrl}
|
||||
mock.recorder = &MockLoggerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Debug mocks base method.
|
||||
func (m *MockLogger) Debug(arg0 string) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Debug", arg0)
|
||||
}
|
||||
|
||||
// Debug indicates an expected call of Debug.
|
||||
func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0)
|
||||
}
|
||||
|
||||
// Debugf mocks base method.
|
||||
func (m *MockLogger) Debugf(arg0 string, arg1 ...interface{}) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{arg0}
|
||||
for _, a := range arg1 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Debugf", varargs...)
|
||||
}
|
||||
|
||||
// Debugf indicates an expected call of Debugf.
|
||||
func (mr *MockLoggerMockRecorder) Debugf(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]interface{}{arg0}, arg1...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...)
|
||||
}
|
||||
|
||||
// Error mocks base method.
|
||||
func (m *MockLogger) Error(arg0 string) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Error", arg0)
|
||||
}
|
||||
|
||||
// Error indicates an expected call of Error.
|
||||
func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0)
|
||||
}
|
||||
|
||||
// Errorf mocks base method.
|
||||
func (m *MockLogger) Errorf(arg0 string, arg1 ...interface{}) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{arg0}
|
||||
for _, a := range arg1 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
m.ctrl.Call(m, "Errorf", varargs...)
|
||||
}
|
||||
|
||||
// Errorf indicates an expected call of Errorf.
|
||||
func (mr *MockLoggerMockRecorder) Errorf(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]interface{}{arg0}, arg1...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...)
|
||||
}
|
||||
|
||||
// Info mocks base method.
|
||||
func (m *MockLogger) Info(arg0 string) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Info", arg0)
|
||||
}
|
||||
|
||||
// Info indicates an expected call of Info.
|
||||
func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/netlink"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination=netlinker_mock_test.go -package amneziawg . NetLinker
|
||||
|
||||
type NetLinker interface {
|
||||
AddrReplace(linkIndex uint32, addr netip.Prefix) error
|
||||
Router
|
||||
Ruler
|
||||
Linker
|
||||
IsWireguardSupported() (ok bool, err error)
|
||||
}
|
||||
|
||||
type Router interface {
|
||||
RouteList(family uint8) (routes []netlink.Route, err error)
|
||||
RouteAdd(route netlink.Route) error
|
||||
}
|
||||
|
||||
type Ruler interface {
|
||||
RuleAdd(rule netlink.Rule) error
|
||||
RuleDel(rule netlink.Rule) error
|
||||
}
|
||||
|
||||
type Linker interface {
|
||||
LinkAdd(link netlink.Link) (linkIndex uint32, err error)
|
||||
LinkList() (links []netlink.Link, err error)
|
||||
LinkByName(name string) (link netlink.Link, err error)
|
||||
LinkSetUp(linkIndex uint32) error
|
||||
LinkSetDown(linkIndex uint32) error
|
||||
LinkDel(linkIndex uint32) error
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/qdm12/gluetun/internal/amneziawg (interfaces: NetLinker)
|
||||
|
||||
// Package amneziawg is a generated GoMock package.
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
netip "net/netip"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
netlink "github.com/qdm12/gluetun/internal/netlink"
|
||||
)
|
||||
|
||||
// MockNetLinker is a mock of NetLinker interface.
|
||||
type MockNetLinker struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockNetLinkerMockRecorder
|
||||
}
|
||||
|
||||
// MockNetLinkerMockRecorder is the mock recorder for MockNetLinker.
|
||||
type MockNetLinkerMockRecorder struct {
|
||||
mock *MockNetLinker
|
||||
}
|
||||
|
||||
// NewMockNetLinker creates a new mock instance.
|
||||
func NewMockNetLinker(ctrl *gomock.Controller) *MockNetLinker {
|
||||
mock := &MockNetLinker{ctrl: ctrl}
|
||||
mock.recorder = &MockNetLinkerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockNetLinker) EXPECT() *MockNetLinkerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// AddrReplace mocks base method.
|
||||
func (m *MockNetLinker) AddrReplace(arg0 uint32, arg1 netip.Prefix) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddrReplace", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddrReplace indicates an expected call of AddrReplace.
|
||||
func (mr *MockNetLinkerMockRecorder) AddrReplace(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrReplace", reflect.TypeOf((*MockNetLinker)(nil).AddrReplace), arg0, arg1)
|
||||
}
|
||||
|
||||
// IsWireguardSupported mocks base method.
|
||||
func (m *MockNetLinker) IsWireguardSupported() (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "IsWireguardSupported")
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// IsWireguardSupported indicates an expected call of IsWireguardSupported.
|
||||
func (mr *MockNetLinkerMockRecorder) IsWireguardSupported() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsWireguardSupported", reflect.TypeOf((*MockNetLinker)(nil).IsWireguardSupported))
|
||||
}
|
||||
|
||||
// LinkAdd mocks base method.
|
||||
func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) (uint32, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LinkAdd", arg0)
|
||||
ret0, _ := ret[0].(uint32)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LinkAdd indicates an expected call of LinkAdd.
|
||||
func (mr *MockNetLinkerMockRecorder) LinkAdd(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), arg0)
|
||||
}
|
||||
|
||||
// LinkByName mocks base method.
|
||||
func (m *MockNetLinker) LinkByName(arg0 string) (netlink.Link, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LinkByName", arg0)
|
||||
ret0, _ := ret[0].(netlink.Link)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LinkByName indicates an expected call of LinkByName.
|
||||
func (mr *MockNetLinkerMockRecorder) LinkByName(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), arg0)
|
||||
}
|
||||
|
||||
// LinkDel mocks base method.
|
||||
func (m *MockNetLinker) LinkDel(arg0 uint32) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LinkDel", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// LinkDel indicates an expected call of LinkDel.
|
||||
func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), arg0)
|
||||
}
|
||||
|
||||
// LinkList mocks base method.
|
||||
func (m *MockNetLinker) LinkList() ([]netlink.Link, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LinkList")
|
||||
ret0, _ := ret[0].([]netlink.Link)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LinkList indicates an expected call of LinkList.
|
||||
func (mr *MockNetLinkerMockRecorder) LinkList() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkList", reflect.TypeOf((*MockNetLinker)(nil).LinkList))
|
||||
}
|
||||
|
||||
// LinkSetDown mocks base method.
|
||||
func (m *MockNetLinker) LinkSetDown(arg0 uint32) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LinkSetDown", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// LinkSetDown indicates an expected call of LinkSetDown.
|
||||
func (mr *MockNetLinkerMockRecorder) LinkSetDown(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), arg0)
|
||||
}
|
||||
|
||||
// LinkSetUp mocks base method.
|
||||
func (m *MockNetLinker) LinkSetUp(arg0 uint32) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LinkSetUp", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// LinkSetUp indicates an expected call of LinkSetUp.
|
||||
func (mr *MockNetLinkerMockRecorder) LinkSetUp(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), arg0)
|
||||
}
|
||||
|
||||
// RouteAdd mocks base method.
|
||||
func (m *MockNetLinker) RouteAdd(arg0 netlink.Route) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RouteAdd", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RouteAdd indicates an expected call of RouteAdd.
|
||||
func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0)
|
||||
}
|
||||
|
||||
// RouteList mocks base method.
|
||||
func (m *MockNetLinker) RouteList(arg0 byte) ([]netlink.Route, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RouteList", arg0)
|
||||
ret0, _ := ret[0].([]netlink.Route)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// RouteList indicates an expected call of RouteList.
|
||||
func (mr *MockNetLinkerMockRecorder) RouteList(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0)
|
||||
}
|
||||
|
||||
// RuleAdd mocks base method.
|
||||
func (m *MockNetLinker) RuleAdd(arg0 netlink.Rule) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RuleAdd", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RuleAdd indicates an expected call of RuleAdd.
|
||||
func (mr *MockNetLinkerMockRecorder) RuleAdd(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), arg0)
|
||||
}
|
||||
|
||||
// RuleDel mocks base method.
|
||||
func (m *MockNetLinker) RuleDel(arg0 netlink.Rule) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RuleDel", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RuleDel indicates an expected call of RuleDel.
|
||||
func (mr *MockNetLinkerMockRecorder) RuleDel(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), arg0)
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
amneziaconn "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
amneziadevice "github.com/amnezia-vpn/amneziawg-go/device"
|
||||
amneziatun "github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/qdm12/gluetun/internal/cleanup"
|
||||
"github.com/qdm12/gluetun/internal/wireguard"
|
||||
)
|
||||
|
||||
var (
|
||||
errTunNameMismatch = errors.New("TUN device name is mismatching")
|
||||
errDeviceWaited = errors.New("device waited for")
|
||||
)
|
||||
|
||||
// Run runs the amneziawg interface and waits until the context is done, then it cleans up the
|
||||
// interface and returns any error that occurred during setup or waiting. It sends an error to
|
||||
// waitError if any error occurs during setup or waiting, otherwise it sends nil when the context
|
||||
// is done. It sends a signal to ready when the setup is complete and the interface is ready to use.
|
||||
// See https://github.com/amnezia-vpn/amneziawg-go/blob/master/main.go
|
||||
func (a *Amneziawg) Run(ctx context.Context, waitError chan<- error, ready chan<- struct{}) {
|
||||
setup := func(ctx context.Context, cleanups *cleanup.Cleanups) (
|
||||
linkIndex uint32, waitAndCleanup func() error, err error,
|
||||
) {
|
||||
return setupUserspace(ctx, a.settings.Wireguard.InterfaceName,
|
||||
a.netlink, a.settings.Wireguard.MTU, cleanups, a.logger, a.settings)
|
||||
}
|
||||
|
||||
wireguard.Run(ctx, waitError, ready, setup, a.settings.Wireguard, a.netlink, a.logger)
|
||||
}
|
||||
|
||||
func setupUserspace(ctx context.Context,
|
||||
interfaceName string, netLinker NetLinker, mtu uint32,
|
||||
cleanups *cleanup.Cleanups, logger Logger,
|
||||
settings Settings,
|
||||
) (
|
||||
linkIndex uint32, waitAndCleanup func() error, err error,
|
||||
) {
|
||||
tun, err := amneziatun.CreateTUN(interfaceName, int(mtu))
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("creating TUN device: %w", err)
|
||||
}
|
||||
|
||||
cleanups.Add("closing TUN device", 7, tun.Close)
|
||||
|
||||
tunName, err := tun.Name()
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("getting created TUN device name: %w", err)
|
||||
} else if tunName != interfaceName {
|
||||
return 0, nil, fmt.Errorf("%w: expected %q and got %q",
|
||||
errTunNameMismatch, interfaceName, tunName)
|
||||
}
|
||||
|
||||
link, err := netLinker.LinkByName(interfaceName)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("finding link %s: %w", interfaceName, err)
|
||||
}
|
||||
cleanups.Add("deleting link", 5, func() error {
|
||||
return netLinker.LinkDel(link.Index)
|
||||
})
|
||||
|
||||
bind := amneziaconn.NewDefaultBind()
|
||||
cleanups.Add("closing bind", 7, bind.Close)
|
||||
|
||||
deviceLogger := amneziadevice.Logger{
|
||||
Verbosef: logger.Debugf,
|
||||
Errorf: logger.Errorf,
|
||||
}
|
||||
device := amneziadevice.NewDevice(tun, bind, &deviceLogger)
|
||||
|
||||
cleanups.Add("closing Wireguard device", 6, func() error {
|
||||
device.Close()
|
||||
return nil
|
||||
})
|
||||
|
||||
uapiFile, err := wireguard.UAPIOpen(interfaceName)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("opening UAPI socket: %w", err)
|
||||
}
|
||||
cleanups.Add("closing UAPI file", 3, uapiFile.Close)
|
||||
|
||||
uapiListener, err := wireguard.UAPIListen(interfaceName, uapiFile)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("listening on UAPI socket: %w", err)
|
||||
}
|
||||
cleanups.Add("closing UAPI listener", 2, uapiListener.Close)
|
||||
|
||||
uapiConfig := settings.uapiConfig()
|
||||
err = device.IpcSet(uapiConfig)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("setting amneziawg uapi config: %w", err)
|
||||
}
|
||||
|
||||
// acceptAndHandle exits when uapiListener is closed
|
||||
uapiAcceptErrorCh := make(chan error)
|
||||
go acceptAndHandle(uapiListener, device, uapiAcceptErrorCh)
|
||||
waitAndCleanup = func() error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case err = <-uapiAcceptErrorCh:
|
||||
close(uapiAcceptErrorCh)
|
||||
case <-device.Wait():
|
||||
err = errDeviceWaited
|
||||
}
|
||||
|
||||
cleanups.Cleanup(logger)
|
||||
|
||||
<-uapiAcceptErrorCh // wait for acceptAndHandle to exit
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return link.Index, waitAndCleanup, nil
|
||||
}
|
||||
|
||||
func acceptAndHandle(uapi net.Listener, device *amneziadevice.Device,
|
||||
uapiAcceptErrorCh chan<- error,
|
||||
) {
|
||||
for { // stopped by uapiFile.Close()
|
||||
conn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
uapiAcceptErrorCh <- err
|
||||
return
|
||||
}
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package amneziawg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/wireguard"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Wireguard wireguard.Settings
|
||||
JunkPacketCount uint16
|
||||
JunkPacketMin uint16
|
||||
JunkPacketMax uint16
|
||||
PaddingS1 uint16
|
||||
PaddingS2 uint16
|
||||
PaddingS3 uint16
|
||||
PaddingS4 uint16
|
||||
HeaderH1 string
|
||||
HeaderH2 string
|
||||
HeaderH3 string
|
||||
HeaderH4 string
|
||||
InitPacketI1 string
|
||||
InitPacketI2 string
|
||||
InitPacketI3 string
|
||||
InitPacketI4 string
|
||||
InitPacketI5 string
|
||||
}
|
||||
|
||||
func (s Settings) uapiConfig() string {
|
||||
uintFields := map[string]uint16{
|
||||
"jc": s.JunkPacketCount,
|
||||
"jmin": s.JunkPacketMin,
|
||||
"jmax": s.JunkPacketMax,
|
||||
"s1": s.PaddingS1,
|
||||
"s2": s.PaddingS2,
|
||||
"s3": s.PaddingS3,
|
||||
"s4": s.PaddingS4,
|
||||
}
|
||||
stringFields := map[string]string{
|
||||
"h1": s.HeaderH1,
|
||||
"h2": s.HeaderH2,
|
||||
"h3": s.HeaderH3,
|
||||
"h4": s.HeaderH4,
|
||||
"i1": s.InitPacketI1,
|
||||
"i2": s.InitPacketI2,
|
||||
"i3": s.InitPacketI3,
|
||||
"i4": s.InitPacketI4,
|
||||
"i5": s.InitPacketI5,
|
||||
}
|
||||
lines := make([]string, 0, len(uintFields)+len(stringFields))
|
||||
|
||||
for key, val := range uintFields {
|
||||
lines = append(lines, fmt.Sprintf("%s=%d", key, val))
|
||||
}
|
||||
|
||||
for key, val := range stringFields {
|
||||
lines = append(lines, key+"="+val)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (s *Settings) SetDefaults() {
|
||||
s.Wireguard.SetDefaults()
|
||||
}
|
||||
|
||||
func (s *Settings) Check() error {
|
||||
return s.Wireguard.Check()
|
||||
}
|
||||
Reference in New Issue
Block a user