diff --git a/go.mod b/go.mod index 3767b5b..a3ffe11 100644 --- a/go.mod +++ b/go.mod @@ -28,4 +28,5 @@ require ( golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 ) diff --git a/go.sum b/go.sum index c972906..1152cfa 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4 github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -131,7 +133,9 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7Zo github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -144,6 +148,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= @@ -166,6 +171,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -174,12 +180,19 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2 golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8= +golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 h1:KTi97NIQGgSMaN0v/oxniJV0MEzfzmrDUOAWxombQVc= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/netconfig/wireguard.go b/internal/netconfig/wireguard.go index 1b85016..ae4c25b 100644 --- a/internal/netconfig/wireguard.go +++ b/internal/netconfig/wireguard.go @@ -24,9 +24,9 @@ import ( "path/filepath" "syscall" - "github.com/mdlayher/genetlink" - "github.com/rtr7/router7/internal/wg" "github.com/vishvananda/netlink" + "golang.zx2c4.com/wireguard/wgctrl" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) type wireguardPeer struct { @@ -79,11 +79,11 @@ func applyWireGuard(dir string) error { } defer h.Delete() - conn, err := genetlink.Dial(nil) + cl, err := wgctrl.New() if err != nil { - return fmt.Errorf("genetlink.Dial: %v", err) + return err } - defer conn.Close() + defer cl.Close() for _, iface := range cfg.Interfaces { l := &wgLink{iface.Name} @@ -93,38 +93,55 @@ func applyWireGuard(dir string) error { } } - var peers []*wg.Peer + var peers []wgtypes.PeerConfig for _, p := range iface.Peers { - var ips []*net.IPNet + var ips []net.IPNet for _, ip := range p.AllowedIPs { _, ipnet, err := net.ParseCIDR(ip) if err != nil { return err } - ips = append(ips, ipnet) + ips = append(ips, *ipnet) } b, err := base64.StdEncoding.DecodeString(p.PublicKey) if err != nil { return err } - peers = append(peers, &wg.Peer{ - PublicKey: b, - Endpoint: p.Endpoint, - AllowedIPs: ips, + publicKey, err := wgtypes.NewKey(b) + if err != nil { + return err + } + var addr *net.UDPAddr + if p.Endpoint != "" { + addr, err = net.ResolveUDPAddr("udp", p.Endpoint) + if err != nil { + return err + } + } + peers = append(peers, wgtypes.PeerConfig{ + PublicKey: publicKey, + Endpoint: addr, + ReplaceAllowedIPs: true, // replace instead of appending + AllowedIPs: ips, }) } b, err := base64.StdEncoding.DecodeString(iface.PrivateKey) if err != nil { return err } - d := &wg.Device{ - Ifname: iface.Name, - PrivateKey: b, - ListenPort: uint16(iface.Port), - Peers: peers, + privateKey, err := wgtypes.NewKey(b) + if err != nil { + return err } - if err := wg.SetDevice(conn, d); err != nil { + err = cl.ConfigureDevice(iface.Name, wgtypes.Config{ + PrivateKey: &privateKey, + ListenPort: &iface.Port, + ReplacePeers: true, // replace instead of appending + // Peers specifies a list of peer configurations to apply to a device. + Peers: peers, + }) + if err != nil { return err } } diff --git a/internal/wg/setdevice.go b/internal/wg/setdevice.go deleted file mode 100644 index d0ff37c..0000000 --- a/internal/wg/setdevice.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package wg - -import ( - "fmt" - "net" - "unsafe" - - "github.com/google/nftables/binaryutil" - "github.com/mdlayher/genetlink" - "github.com/mdlayher/netlink" - "golang.org/x/sys/unix" -) - -func allowedIPFromNet(n *net.IPNet) ([]byte, error) { - ones, _ := n.Mask.Size() - family := uint16(unix.AF_INET) - if n.IP.To4() == nil { - family = unix.AF_INET6 - } - return netlink.MarshalAttributes([]netlink.Attribute{ - {Type: wgallowedip_a_family, Data: binaryutil.NativeEndian.PutUint16(family)}, - {Type: wgallowedip_a_ipaddr, Data: n.IP}, - {Type: wgallowedip_a_cidr_mask, Data: []byte{byte(ones)}}, - }) -} - -func sockaddrFromEndpoint(endpoint string) ([]byte, error) { - host, service, err := net.SplitHostPort(endpoint) - if err != nil { - return nil, err - } - ip := net.ParseIP(host) - if ip == nil { - return nil, fmt.Errorf("invalid endpoint %q: %q is not an IP", endpoint, host) - } - port, err := net.LookupPort("udp4", service) - if err != nil { - return nil, err - } - if ip.To4() == nil { - addr := unix.RawSockaddrInet6{ - Family: unix.AF_INET6, - Port: uint16((port&0xFF)<<8) | uint16((port&0xFF00)>>8), - Addr: func() [16]byte { - var buf [16]byte - copy(buf[:], ip) - return buf - }(), - } - sap := (*[28]byte)(unsafe.Pointer(&addr)) - return (*sap)[:], nil - } else { - addr := unix.RawSockaddrInet4{ - Family: unix.AF_INET, - Port: uint16((port&0xFF)<<8) | uint16((port&0xFF00)>>8), - Addr: func() [4]byte { - var buf [4]byte - copy(buf[:], ip.To4()) - return buf - }(), - } - sap := (*[16]byte)(unsafe.Pointer(&addr)) - return (*sap)[:], nil - } -} - -func SetDevice(conn *genetlink.Conn, d *Device) error { - family, err := conn.GetFamily("wireguard") - if err != nil { - return err - } - - var peers []netlink.Attribute - for _, p := range d.Peers { - var ips []netlink.Attribute - for _, net := range p.AllowedIPs { - allowedIP, err := allowedIPFromNet(net) - if err != nil { - return err - } - ips = append(ips, netlink.Attribute{Type: unix.NLA_F_NESTED, Data: allowedIP}) - } - allowedIPs, err := netlink.MarshalAttributes(ips) - if err != nil { - return err - } - - attrs := []netlink.Attribute{ - {Type: wgpeer_a_public_key, Data: p.PublicKey}, - {Type: wgpeer_a_flags, Data: binaryutil.NativeEndian.PutUint32(0)}, - - {Type: wgpeer_a_persistent_keepalive_interval, Data: binaryutil.NativeEndian.PutUint16(0)}, - {Type: wgpeer_a_allowedips, Data: allowedIPs}, - } - if p.Endpoint != "" { - sockaddr, err := sockaddrFromEndpoint(p.Endpoint) - if err != nil { - return err - } - attrs = append(attrs, netlink.Attribute{Type: wgpeer_a_endpoint, Data: sockaddr}) - } - peer, err := netlink.MarshalAttributes(attrs) - if err != nil { - return err - } - peers = append(peers, netlink.Attribute{Type: unix.NLA_F_NESTED, Data: peer}) - } - peersData, err := netlink.MarshalAttributes(peers) - if err != nil { - return err - } - - data, err := netlink.MarshalAttributes([]netlink.Attribute{ - {Type: wgdevice_a_ifname, Data: []byte(d.Ifname + "\x00")}, - {Type: wgdevice_a_flags, Data: binaryutil.NativeEndian.PutUint32(0)}, - {Type: wgdevice_a_private_key, Data: d.PrivateKey}, - {Type: wgdevice_a_listen_port, Data: binaryutil.NativeEndian.PutUint16(d.ListenPort)}, - {Type: wgdevice_a_fwmark, Data: binaryutil.NativeEndian.PutUint32(0)}, - {Type: unix.NLA_F_NESTED | wgdevice_a_peers, Data: peersData}, - }) - if err != nil { - return err - } - get := genetlink.Message{ - Header: genetlink.Header{ - Command: wg_cmd_set_device, - Version: family.Version, - }, - Data: data, - } - - const flags = netlink.Request | netlink.Acknowledge - reply, err := conn.Execute(get, family.ID, flags) - if err != nil { - return err - } - if got, want := len(reply), 1; got != want { - return fmt.Errorf("unexpected number of replies: got %d, want %d", got, want) - } - return nil -} diff --git a/internal/wg/types.go b/internal/wg/types.go deleted file mode 100644 index 3c108a5..0000000 --- a/internal/wg/types.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package wg implements the WireGuard Linux kernel module’s generic netlink -// interface, e.g. for configuring WireGuard network interfaces (e.g. wg0) -// without resorting to the wg command line tool. -package wg - -import ( - "net" - "time" -) - -const ( - wg_cmd_get_device = iota - wg_cmd_set_device -) -const ( - wgdevice_a_unspec = iota - wgdevice_a_ifindex - wgdevice_a_ifname - wgdevice_a_private_key - wgdevice_a_public_key - wgdevice_a_flags - wgdevice_a_listen_port - wgdevice_a_fwmark - wgdevice_a_peers -) - -const ( - wgpeer_a_unspec = iota - wgpeer_a_public_key - wgpeer_a_preshared_key - wgpeer_a_flags - wgpeer_a_endpoint - wgpeer_a_persistent_keepalive_interval - wgpeer_a_last_handshake_time - wgpeer_a_rx_bytes - wgpeer_a_tx_bytes - wgpeer_a_allowedips - wgpeer_a_protocol_version -) - -const ( - wgallowedip_a_unspec = iota - wgallowedip_a_family - wgallowedip_a_ipaddr - wgallowedip_a_cidr_mask -) - -type Peer struct { - PublicKey []byte - PresharedKey []byte - PersistentKeepaliveInterval uint16 - LastHandshakeTime time.Time - RxBytes uint64 - TxBytes uint64 - AllowedIPs []*net.IPNet - ProtocolVersion uint32 - Endpoint string -} - -type Device struct { - Ifindex uint32 - Ifname string - PrivateKey []byte - PublicKey []byte - ListenPort uint16 - Fwmark uint32 - Peers []*Peer -}