2019-03-27 17:26:09 +01:00
|
|
|
package wgquick
|
2019-03-26 13:44:38 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/mdlayher/wireguardctrl"
|
2019-03-26 13:57:33 +01:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-03-26 13:44:38 +01:00
|
|
|
"github.com/vishvananda/netlink"
|
2019-03-27 17:26:09 +01:00
|
|
|
"net"
|
|
|
|
"syscall"
|
2019-03-26 13:44:38 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-03-26 13:57:33 +01:00
|
|
|
// Sync the config to the current setup for given interface
|
|
|
|
func (cfg *Config) Sync(iface string, logger logrus.FieldLogger) error {
|
|
|
|
log := logger.WithField("iface", iface)
|
2019-03-26 13:44:38 +01:00
|
|
|
link, err := netlink.LinkByName(iface)
|
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(netlink.LinkNotFoundError); !ok {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot read link")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Info("link not found, creating")
|
|
|
|
wgLink := &netlink.GenericLink{
|
|
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
|
|
Name: iface,
|
2019-03-26 13:58:26 +01:00
|
|
|
MTU: cfg.MTU,
|
2019-03-26 13:44:38 +01:00
|
|
|
},
|
|
|
|
LinkType: "wireguard",
|
|
|
|
}
|
|
|
|
if err := netlink.LinkAdd(wgLink); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot create link")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
link, err = netlink.LinkByName(iface)
|
|
|
|
if err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot read link")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := netlink.LinkSetUp(link); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot set link up")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("set device up")
|
2019-03-26 13:44:38 +01:00
|
|
|
|
|
|
|
cl, err := wireguardctrl.New()
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "cannot setup wireguard device")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := cl.ConfigureDevice(iface, cfg.Config); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot configure device")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-26 13:57:33 +01:00
|
|
|
if err := syncAddress(link, cfg, log); err != nil {
|
2019-03-26 13:44:38 +01:00
|
|
|
log.Error(err, "cannot sync addresses")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-26 13:57:33 +01:00
|
|
|
if err := syncRoutes(link, cfg, log); err != nil {
|
2019-03-26 13:44:38 +01:00
|
|
|
log.Error(err, "cannot sync routes")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-26 14:35:31 +01:00
|
|
|
log.Info("Successfully setup device")
|
2019-03-26 13:44:38 +01:00
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-03-26 13:57:33 +01:00
|
|
|
func syncAddress(link netlink.Link, cfg *Config, log logrus.FieldLogger) error {
|
2019-03-26 13:44:38 +01:00
|
|
|
addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "cannot read link address")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
presentAddresses := make(map[string]int, 0)
|
|
|
|
for _, addr := range addrs {
|
|
|
|
presentAddresses[addr.IPNet.String()] = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, addr := range cfg.Address {
|
2019-03-26 13:57:33 +01:00
|
|
|
log := log.WithField("addr", addr)
|
2019-03-26 13:44:38 +01:00
|
|
|
_, present := presentAddresses[addr.String()]
|
|
|
|
presentAddresses[addr.String()] = 2
|
|
|
|
if present {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("address present")
|
2019-03-26 13:44:38 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := netlink.AddrAdd(link, &netlink.Addr{
|
|
|
|
IPNet: addr,
|
|
|
|
}); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot add addr")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("address added")
|
2019-03-26 13:44:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for addr, p := range presentAddresses {
|
2019-03-26 13:57:33 +01:00
|
|
|
log := log.WithField("addr", addr)
|
2019-03-26 13:44:38 +01:00
|
|
|
if p < 2 {
|
|
|
|
nlAddr, err := netlink.ParseAddr(addr)
|
|
|
|
if err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot parse del addr")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := netlink.AddrAdd(link, nlAddr); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot delete addr")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("addr deleted")
|
2019-03-26 13:44:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-26 13:57:33 +01:00
|
|
|
func syncRoutes(link netlink.Link, cfg *Config, log logrus.FieldLogger) error {
|
2019-03-26 13:44:38 +01:00
|
|
|
routes, err := netlink.RouteList(link, syscall.AF_INET)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "cannot read existing routes")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
presentRoutes := make(map[string]int, 0)
|
|
|
|
for _, r := range routes {
|
2019-03-26 14:35:31 +01:00
|
|
|
log := log.WithField("route", r.Dst.String())
|
2019-03-26 14:53:14 +01:00
|
|
|
if r.Table == cfg.Table || cfg.Table == 0 {
|
2019-03-26 14:01:02 +01:00
|
|
|
presentRoutes[r.Dst.String()] = 1
|
2019-03-26 14:53:14 +01:00
|
|
|
log.WithField("table", r.Table).Debug("detected existing route")
|
2019-03-26 14:35:31 +01:00
|
|
|
} else {
|
|
|
|
log.Debug("wrong table for route, skipping")
|
2019-03-26 14:01:02 +01:00
|
|
|
}
|
2019-03-26 13:44:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, peer := range cfg.Peers {
|
|
|
|
for _, rt := range peer.AllowedIPs {
|
|
|
|
_, present := presentRoutes[rt.String()]
|
|
|
|
presentRoutes[rt.String()] = 2
|
2019-03-26 13:57:33 +01:00
|
|
|
log := log.WithField("route", rt.String())
|
2019-03-26 13:44:38 +01:00
|
|
|
if present {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("route present")
|
2019-03-26 13:44:38 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := netlink.RouteAdd(&netlink.Route{
|
|
|
|
LinkIndex: link.Attrs().Index,
|
|
|
|
Dst: &rt,
|
2019-03-26 14:01:02 +01:00
|
|
|
Table: cfg.Table,
|
2019-03-26 13:44:38 +01:00
|
|
|
}); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot setup route")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("route added")
|
2019-03-26 13:44:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean extra routes
|
|
|
|
for rtStr, p := range presentRoutes {
|
|
|
|
_, rt, err := net.ParseCIDR(rtStr)
|
2019-03-26 13:57:33 +01:00
|
|
|
log := log.WithField("route", rt.String())
|
2019-03-26 13:44:38 +01:00
|
|
|
if err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot parse route")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if p < 2 {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("extra manual route found")
|
2019-03-26 13:44:38 +01:00
|
|
|
if err := netlink.RouteDel(&netlink.Route{
|
|
|
|
LinkIndex: link.Attrs().Index,
|
|
|
|
Dst: rt,
|
2019-03-26 14:01:02 +01:00
|
|
|
Table: cfg.Table,
|
2019-03-26 13:44:38 +01:00
|
|
|
}); err != nil {
|
2019-03-26 13:57:33 +01:00
|
|
|
log.WithError(err).Error("cannot setup route")
|
2019-03-26 13:44:38 +01:00
|
|
|
return err
|
|
|
|
}
|
2019-03-26 13:57:33 +01:00
|
|
|
log.Info("route deleted")
|
2019-03-26 13:44:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|