router7/internal/netconfig/wireguard.go

134 lines
3.1 KiB
Go
Raw Normal View History

// 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 netconfig
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"syscall"
"github.com/mdlayher/genetlink"
"github.com/rtr7/router7/internal/wg"
"github.com/vishvananda/netlink"
)
type wireguardPeer struct {
PublicKey string `json:"public_key"` // base64-encoded
Endpoint string `json:"endpoint"` // e.g. “[::1]:12345”
AllowedIPs []string `json:"allowed_ips"` // e.g. “["fe80::/64", "10.0.137.0/24"]”
}
type wireguardInterface struct {
Name string `json:"name"` // e.g. “wg0”
PrivateKey string `json:"private_key"` // base64-encoded
Port int `json:"port"` // e.g. “51820”
Peers []wireguardPeer `json:"peers"`
}
type wireguardInterfaces struct {
Interfaces []wireguardInterface `json:"interfaces"`
}
type wgLink struct {
name string
}
func (w *wgLink) Type() string {
return "wireguard"
}
func (w *wgLink) Attrs() *netlink.LinkAttrs {
attrs := netlink.NewLinkAttrs()
attrs.Name = w.name
return &attrs
}
func applyWireGuard(dir string) error {
b, err := ioutil.ReadFile(filepath.Join(dir, "wireguard.json"))
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
var cfg wireguardInterfaces
if err := json.Unmarshal(b, &cfg); err != nil {
return err
}
h, err := netlink.NewHandle()
if err != nil {
return fmt.Errorf("netlink.NewHandle: %v", err)
}
defer h.Delete()
conn, err := genetlink.Dial(nil)
if err != nil {
return fmt.Errorf("genetlink.Dial: %v", err)
}
defer conn.Close()
for _, iface := range cfg.Interfaces {
l := &wgLink{iface.Name}
if err := h.LinkAdd(l); err != nil {
if ee, ok := err.(syscall.Errno); !ok || ee != syscall.EEXIST {
return fmt.Errorf("LinkAdd(%v): %v", l, err)
}
}
var peers []*wg.Peer
for _, p := range iface.Peers {
var ips []*net.IPNet
for _, ip := range p.AllowedIPs {
_, ipnet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
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,
})
}
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,
}
if err := wg.SetDevice(conn, d); err != nil {
return err
}
}
return nil
}