diff --git a/cluster/cluster.go b/cluster/cluster.go index 67be2f8..c8a0904 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -14,8 +14,10 @@ import ( "github.com/sirupsen/logrus" ) +// KeyLen is the fixed length of cluster keys, must be checked by callers const KeyLen = 32 +// Cluster represents a running cluster configuration type Cluster struct { LocalName string // used to avoid LocalNode(); should not change ml *memberlist.Memberlist @@ -24,6 +26,8 @@ type Cluster struct { events chan memberlist.NodeEvent } +// New is used to create a new Cluster instance +// The returned instance is ready to be updated with the local node settings then joined func New(init bool, clusterKey []byte, bindAddr string, bindPort int, useIPAsName bool) (*Cluster, error) { state := &State{} if !init { @@ -65,6 +69,11 @@ func New(init bool, clusterKey []byte, bindAddr string, bindPort int, useIPAsNam return &cluster, nil } +// Join tries to join the cluster by contacting provided addresses +// Provided addresses are passed as is, if no address is provided, known +// cluster nodes are contacted instead. +// Joining fail if none of the provided addresses or none of the known +// nodes can be joined. func (c *Cluster) Join(addrs []string) error { if len(addrs) == 0 { for _, n := range c.state.Nodes { @@ -80,17 +89,24 @@ func (c *Cluster) Join(addrs []string) error { return nil } +// Leave saves the current state before leaving, then leaves the cluster func (c *Cluster) Leave() { c.saveState() c.ml.Leave(10 * time.Second) c.ml.Shutdown() //nolint: errcheck } +// Update takes a new local node configuration into account +// If the node is already joined, update also gossips the new local node +// configuration func (c *Cluster) Update(localNode common.Node) { c.localNode = localNode c.ml.UpdateNode(1 * time.Second) // we currently do not update after creation } +// Members provides a channel notifying of cluster changes +// Everytime a change happens inside the cluster (except for local changes), +// the updated list of cluster nodes is pushed to the channel. func (c *Cluster) Members() <-chan []common.Node { changes := make(chan []common.Node) go func() { diff --git a/cluster/delegate.go b/cluster/delegate.go index b7da044..fa78b09 100644 --- a/cluster/delegate.go +++ b/cluster/delegate.go @@ -5,10 +5,14 @@ import ( "github.com/sirupsen/logrus" ) +// NotifyConflict implements the memberlist deletage interface func (c *Cluster) NotifyConflict(node, other *memberlist.Node) { logrus.Errorf("node name conflict detected: %s", other.Name) } +// NodeMeta implements the memberlist deletage interface +// Metadata is provided by the local node settings, encoding is handled +// by the node implementation directly func (c *Cluster) NodeMeta(limit int) []byte { encoded, err := c.localNode.Encode(limit) if err != nil { @@ -18,8 +22,14 @@ func (c *Cluster) NodeMeta(limit int) []byte { return encoded } -// none of these are used -func (c *Cluster) NotifyMsg([]byte) {} +// NotifyMsg implements the memberlist deletage interface +func (c *Cluster) NotifyMsg([]byte) {} + +// GetBroadcasts implements the memberlist deletage interface func (c *Cluster) GetBroadcasts(overhead, limit int) [][]byte { return nil } -func (c *Cluster) LocalState(join bool) []byte { return nil } -func (c *Cluster) MergeRemoteState(buf []byte, join bool) {} + +// LocalState implements the memberlist deletage interface +func (c *Cluster) LocalState(join bool) []byte { return nil } + +// MergeRemoteState implements the memberlist deletage interface +func (c *Cluster) MergeRemoteState(buf []byte, join bool) {} diff --git a/common/node.go b/common/node.go index 8f86e8a..4babcc2 100644 --- a/common/node.go +++ b/common/node.go @@ -38,6 +38,7 @@ func (n *Node) Encode(limit int) ([]byte, error) { return buf.Bytes(), nil } +// Decode the node Meta field into its metadata func (n *Node) Decode() error { // TODO: we blindly trust the info we get from the peers; We should be more defensive to limit the damage a leaked // PSK can cause. diff --git a/wg/wireguard.go b/wg/wireguard.go index 42357d1..494bfa6 100644 --- a/wg/wireguard.go +++ b/wg/wireguard.go @@ -12,6 +12,7 @@ import ( "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) +// WgState holds the configured state of a Wesher Wireguard interface type WgState struct { iface string client *wgctrl.Client @@ -21,6 +22,9 @@ type WgState struct { PubKey wgtypes.Key } +// NewWGConfig creates a new Wesher Wireguard state +// The Wireguard keys are generated for every new interface +// The interface must later be setup using SetUpInterface func NewWGConfig(iface string, port int) (*WgState, error) { client, err := wgctrl.New() if err != nil { @@ -43,6 +47,11 @@ func NewWGConfig(iface string, port int) (*WgState, error) { return &wgState, nil } +// AssignOverlayAddr assigns a new address to the interface +// The address is assigned inside the provided network and depends on the +// provided name deterministically +// Currently, the address is assigned by hashing the name and mapping that +// hash in the target network space func (wg *WgState) AssignOverlayAddr(ipnet *net.IPNet, name string) { // TODO: this is way too brittle and opaque bits, size := ipnet.Mask.Size() @@ -63,6 +72,7 @@ func (wg *WgState) AssignOverlayAddr(ipnet *net.IPNet, name string) { } } +// DownInterface shuts down the associated network interface func (wg *WgState) DownInterface() error { if _, err := wg.client.Device(wg.iface); err != nil { if os.IsNotExist(err) { @@ -77,12 +87,13 @@ func (wg *WgState) DownInterface() error { return netlink.LinkDel(link) } +// SetUpInterface creates and setup the associated network interface func (wg *WgState) SetUpInterface(nodes []common.Node) error { if err := netlink.LinkAdd(&wireguard{LinkAttrs: netlink.LinkAttrs{Name: wg.iface}}); err != nil && !os.IsExist(err) { return errors.Wrapf(err, "could not create interface %s", wg.iface) } - peerCfgs, err := wg.NodesToPeerConfigs(nodes) + peerCfgs, err := wg.nodesToPeerConfigs(nodes) if err != nil { return errors.Wrap(err, "error converting received node information to wireguard format") } @@ -122,7 +133,7 @@ func (wg *WgState) SetUpInterface(nodes []common.Node) error { return nil } -func (wg *WgState) NodesToPeerConfigs(nodes []common.Node) ([]wgtypes.PeerConfig, error) { +func (wg *WgState) nodesToPeerConfigs(nodes []common.Node) ([]wgtypes.PeerConfig, error) { peerCfgs := make([]wgtypes.PeerConfig, len(nodes)) for i, node := range nodes { pubKey, err := wgtypes.ParseKey(node.PubKey)