diff --git a/internal/netlink/family.go b/internal/netlink/family.go index 062469f5..605e1b8e 100644 --- a/internal/netlink/family.go +++ b/internal/netlink/family.go @@ -6,4 +6,5 @@ import "github.com/vishvananda/netlink" const ( FAMILY_ALL = netlink.FAMILY_ALL FAMILY_V4 = netlink.FAMILY_V4 + FAMILY_V6 = netlink.FAMILY_V6 ) diff --git a/internal/wireguard/ipv6.go b/internal/wireguard/ipv6.go new file mode 100644 index 00000000..ff008280 --- /dev/null +++ b/internal/wireguard/ipv6.go @@ -0,0 +1,33 @@ +package wireguard + +import ( + "errors" + "fmt" + + "github.com/qdm12/gluetun/internal/netlink" +) + +var ( + errLinkList = errors.New("cannot list links") + errRouteList = errors.New("cannot list routes") +) + +func (w *Wireguard) isIPv6Supported() (supported bool, err error) { + links, err := w.netlink.LinkList() + if err != nil { + return false, fmt.Errorf("%w: %s", errLinkList, err) + } + + for _, link := range links { + routes, err := w.netlink.RouteList(link, netlink.FAMILY_V6) + if err != nil { + return false, fmt.Errorf("%w: %s", errRouteList, err) + } + + if len(routes) > 0 { + return true, nil + } + } + + return false, nil +} diff --git a/internal/wireguard/netlinker.go b/internal/wireguard/netlinker.go index 2b4a67f6..7989ede7 100644 --- a/internal/wireguard/netlinker.go +++ b/internal/wireguard/netlinker.go @@ -6,9 +6,11 @@ import "github.com/qdm12/gluetun/internal/netlink" type NetLinker interface { AddrAdd(link netlink.Link, addr *netlink.Addr) error + RouteList(link netlink.Link, family int) (routes []netlink.Route, err error) RouteAdd(route *netlink.Route) error RuleAdd(rule *netlink.Rule) error RuleDel(rule *netlink.Rule) error + LinkList() (links []netlink.Link, err error) LinkByName(name string) (link netlink.Link, err error) LinkSetUp(link netlink.Link) error LinkSetDown(link netlink.Link) error diff --git a/internal/wireguard/netlinker_mock_test.go b/internal/wireguard/netlinker_mock_test.go index e29f5475..bf9fc94e 100644 --- a/internal/wireguard/netlinker_mock_test.go +++ b/internal/wireguard/netlinker_mock_test.go @@ -77,6 +77,21 @@ func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call { 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 netlink.Link) error { m.ctrl.T.Helper() @@ -119,6 +134,21 @@ func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0) } +// RouteList mocks base method. +func (m *MockNetLinker) RouteList(arg0 netlink.Link, arg1 int) ([]netlink.Route, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RouteList", arg0, arg1) + ret0, _ := ret[0].([]netlink.Route) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RouteList indicates an expected call of RouteList. +func (mr *MockNetLinkerMockRecorder) RouteList(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0, arg1) +} + // RuleAdd mocks base method. func (m *MockNetLinker) RuleAdd(arg0 *netlink.Rule) error { m.ctrl.T.Helper() diff --git a/internal/wireguard/run.go b/internal/wireguard/run.go index db832cf5..f47dad59 100644 --- a/internal/wireguard/run.go +++ b/internal/wireguard/run.go @@ -14,6 +14,7 @@ import ( ) var ( + ErrDetectIPv6 = errors.New("cannot detect IPv6 support") ErrCreateTun = errors.New("cannot create TUN device") ErrFindLink = errors.New("cannot find link") ErrFindDevice = errors.New("cannot find Wireguard device") @@ -34,6 +35,12 @@ type Runner interface { // 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 + } + client, err := wgctrl.New() if err != nil { waitError <- fmt.Errorf("%w: %s", ErrWgctrlOpen, err) @@ -131,6 +138,15 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan< return } + if doIPv6 { + // requires net.ipv6.conf.all.disable_ipv6=0 + err = w.addRoute(link, allIPv6(), w.settings.FirewallMark) + if err != nil { + waitError <- fmt.Errorf("%w: %s", ErrRouteAdd, err) + return + } + } + ruleCleanup, err := w.addRule( w.settings.RulePriority, w.settings.FirewallMark) if err != nil {