wireguard: switch to wgctrl package

related to #14
This commit is contained in:
Michael Stapelberg 2020-02-16 00:14:33 +01:00
parent bfba9f17af
commit dba1dad718
5 changed files with 49 additions and 255 deletions

1
go.mod
View File

@ -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
)

13
go.sum
View File

@ -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=

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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 modules 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
}