Cleanup - WIP
This commit is contained in:
15
peer/app.go
15
peer/app.go
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"vppn/m"
|
||||
"vppn/peer/control"
|
||||
"vppn/peer/wginterface"
|
||||
)
|
||||
@@ -21,18 +22,6 @@ const (
|
||||
TimeoutInterval = 30 * time.Second
|
||||
)
|
||||
|
||||
// HubPeer is a peer entry as reported by the hub poller.
|
||||
type HubPeer struct {
|
||||
PubKey wgtypes.Key
|
||||
VPNIP netip.Addr
|
||||
Name string
|
||||
IsRelay bool
|
||||
IsPublic bool
|
||||
EndpointV4 netip.AddrPort // zero if none
|
||||
EndpointV6 netip.AddrPort // zero if none
|
||||
SignPubKey [32]byte
|
||||
}
|
||||
|
||||
type PingEvent struct {
|
||||
srcVPNIP netip.Addr
|
||||
ping control.Ping
|
||||
@@ -70,7 +59,7 @@ type App struct {
|
||||
selfV6 netip.AddrPort
|
||||
|
||||
// Event channels fed by background goroutines
|
||||
hubAddCh <-chan HubPeer
|
||||
hubAddCh <-chan m.Peer
|
||||
hubRemoveCh <-chan wgtypes.Key
|
||||
pingCh <-chan PingEvent
|
||||
multicastCh <-chan MulticastEvent
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"vppn/m"
|
||||
)
|
||||
|
||||
// addRelayPeer adds a public relay peer and marks it Up so it satisfies
|
||||
@@ -13,12 +15,13 @@ import (
|
||||
func addRelayPeer(t *testing.T, a *App, vpnIP string, ep netip.AddrPort) *Peer {
|
||||
t.Helper()
|
||||
key := mustKey(t)
|
||||
a.onAddPeer(HubPeer{
|
||||
PubKey: key,
|
||||
VPNIP: netip.MustParseAddr(vpnIP),
|
||||
IsPublic: true,
|
||||
IsRelay: true,
|
||||
EndpointV4: ep,
|
||||
ip := netip.MustParseAddr(vpnIP)
|
||||
a.onAddPeer(m.Peer{
|
||||
WGPubKey: key,
|
||||
PeerIP: ip.As4()[3],
|
||||
Addr4: ep.Addr(),
|
||||
Port: ep.Port(),
|
||||
Relay: true,
|
||||
})
|
||||
p := a.peersByKey[key]
|
||||
p.wgPeer.LastHandshakeTime = time.Now()
|
||||
@@ -48,7 +51,7 @@ func newTestApp(t *testing.T, vpnIP string, isPublic, isRelay bool) (*App, *fake
|
||||
controlConn: cc,
|
||||
peersByKey: make(map[wgtypes.Key]*Peer),
|
||||
peersByIP: make(map[netip.Addr]*Peer),
|
||||
hubAddCh: make(chan HubPeer),
|
||||
hubAddCh: make(chan m.Peer),
|
||||
hubRemoveCh: make(chan wgtypes.Key),
|
||||
pingCh: make(chan PingEvent),
|
||||
multicastCh: make(chan MulticastEvent),
|
||||
|
||||
@@ -22,7 +22,7 @@ type HubPoller struct {
|
||||
hubURL string
|
||||
apiKey string
|
||||
statePath string // where the network state cache is persisted
|
||||
addCh chan<- HubPeer
|
||||
addCh chan<- m.Peer
|
||||
removeCh chan<- wgtypes.Key
|
||||
known map[wgtypes.Key]int64 // pubKey → last seen version
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func NewHubPoller(
|
||||
vpnNet netip.Prefix,
|
||||
hubURL, apiKey string,
|
||||
statePath string,
|
||||
addCh chan<- HubPeer,
|
||||
addCh chan<- m.Peer,
|
||||
removeCh chan<- wgtypes.Key,
|
||||
) (*HubPoller, error) {
|
||||
u, err := url.Parse(hubURL)
|
||||
@@ -119,12 +119,7 @@ func (hp *HubPoller) apply(state m.NetworkState) (changed bool) {
|
||||
netAddr := hp.vpnNet.Addr().As4()
|
||||
|
||||
for _, p := range state.Peers {
|
||||
if len(p.WGPubKey) != wgtypes.KeyLen || len(p.SignPubKey) != 32 {
|
||||
continue
|
||||
}
|
||||
|
||||
pubKey, err := wgtypes.NewKey(p.WGPubKey)
|
||||
if err != nil {
|
||||
if p.WGPubKey == (wgtypes.Key{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -135,13 +130,13 @@ func (hp *HubPoller) apply(state m.NetworkState) (changed bool) {
|
||||
continue
|
||||
}
|
||||
|
||||
seen[pubKey] = struct{}{}
|
||||
seen[p.WGPubKey] = struct{}{}
|
||||
|
||||
if v, ok := hp.known[pubKey]; ok && v == p.Version {
|
||||
if v, ok := hp.known[p.WGPubKey]; ok && v == p.Version {
|
||||
continue
|
||||
}
|
||||
hp.known[pubKey] = p.Version
|
||||
hp.addCh <- hubPeerFrom(pubKey, vpnIP, p)
|
||||
hp.known[p.WGPubKey] = p.Version
|
||||
hp.addCh <- p
|
||||
changed = true
|
||||
}
|
||||
|
||||
@@ -155,30 +150,3 @@ func (hp *HubPoller) apply(state m.NetworkState) (changed bool) {
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p m.Peer) HubPeer {
|
||||
var ep4, ep6 netip.AddrPort
|
||||
if len(p.Addr4) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(p.Addr4); ok {
|
||||
ep4 = netip.AddrPortFrom(addr.Unmap(), p.Port)
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.Addr6) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(p.Addr6); ok {
|
||||
ep6 = netip.AddrPortFrom(addr.Unmap(), p.Port)
|
||||
}
|
||||
}
|
||||
var signPubKey [32]byte
|
||||
copy(signPubKey[:], p.SignPubKey)
|
||||
return HubPeer{
|
||||
PubKey: pubKey,
|
||||
VPNIP: vpnIP,
|
||||
Name: p.Name,
|
||||
IsRelay: p.Relay,
|
||||
IsPublic: ep4.IsValid() || ep6.IsValid(),
|
||||
EndpointV4: ep4,
|
||||
EndpointV6: ep6,
|
||||
SignPubKey: signPubKey,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"vppn/m"
|
||||
)
|
||||
|
||||
func testPoller(t *testing.T) (*HubPoller, chan HubPeer, chan wgtypes.Key) {
|
||||
func testPoller(t *testing.T) (*HubPoller, chan m.Peer, chan wgtypes.Key) {
|
||||
t.Helper()
|
||||
addCh := make(chan HubPeer, 8)
|
||||
addCh := make(chan m.Peer, 8)
|
||||
removeCh := make(chan wgtypes.Key, 8)
|
||||
hp := &HubPoller{
|
||||
selfVPNIP: netip.MustParseAddr("10.0.0.1"),
|
||||
@@ -25,10 +25,9 @@ func testPoller(t *testing.T) (*HubPoller, chan HubPeer, chan wgtypes.Key) {
|
||||
|
||||
func stateWith(key wgtypes.Key, peerIP byte, version int64) m.NetworkState {
|
||||
return m.NetworkState{Peers: []m.Peer{{
|
||||
PeerIP: peerIP,
|
||||
Version: version,
|
||||
WGPubKey: key[:],
|
||||
SignPubKey: make([]byte, 32),
|
||||
PeerIP: peerIP,
|
||||
Version: version,
|
||||
WGPubKey: key,
|
||||
}}}
|
||||
}
|
||||
|
||||
@@ -42,7 +41,7 @@ func TestApply_EmitsAddsAndReportsChange(t *testing.T) {
|
||||
if len(addCh) != 1 {
|
||||
t.Fatalf("expected 1 add, got %d", len(addCh))
|
||||
}
|
||||
if got := <-addCh; got.PubKey != key {
|
||||
if got := <-addCh; got.WGPubKey != key {
|
||||
t.Errorf("add pubkey mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
27
peer/init.go
27
peer/init.go
@@ -78,10 +78,13 @@ func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error)
|
||||
return LocalState{}, fmt.Errorf("generate sign key: %w", err)
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(m.PeerInitArgs{
|
||||
body, err := json.Marshal(m.PeerInitArgs{
|
||||
WGPubKey: wgPubKey[:],
|
||||
SignPubKey: signPubKey[:],
|
||||
})
|
||||
if err != nil {
|
||||
return LocalState{}, fmt.Errorf("json error: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, hubURL+"/peer/init/", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
@@ -126,31 +129,15 @@ func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error)
|
||||
return LocalState{}, fmt.Errorf("hub init: no peer for own IP: %d", r.PeerIP)
|
||||
}
|
||||
|
||||
var isRelay, public bool
|
||||
var wgPort uint16
|
||||
|
||||
var ep4, ep6 netip.AddrPort
|
||||
if len(self.Addr4) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(self.Addr4); ok {
|
||||
ep4 = netip.AddrPortFrom(addr.Unmap(), self.Port)
|
||||
}
|
||||
}
|
||||
if len(self.Addr6) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(self.Addr6); ok {
|
||||
ep6 = netip.AddrPortFrom(addr.Unmap(), self.Port)
|
||||
}
|
||||
}
|
||||
public = ep4.IsValid() || ep6.IsValid()
|
||||
isRelay = self.Relay && public
|
||||
wgPort = self.Port
|
||||
public := self.IsPublic()
|
||||
|
||||
return LocalState{
|
||||
PrivKey: privKey,
|
||||
SignKey: *signPrivKey,
|
||||
VPNIP: vpnIP,
|
||||
VPNNet: vpnNet,
|
||||
WGPort: wgPort,
|
||||
IsRelay: isRelay,
|
||||
WGPort: self.Port,
|
||||
IsRelay: self.Relay && public,
|
||||
IsPublic: public,
|
||||
LocalDomain: r.LocalDomain,
|
||||
}, nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package peer
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
@@ -11,25 +12,27 @@ import (
|
||||
func TestNetworkState_RoundTrip(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "network.json")
|
||||
|
||||
var sign1 [32]byte
|
||||
copy(sign1[:], []byte("0123456789abcdef0123456789abcdef"))
|
||||
|
||||
state := m.NetworkState{Peers: []m.Peer{
|
||||
{
|
||||
PeerIP: 1,
|
||||
Version: 7,
|
||||
Name: "hub",
|
||||
Addr4: []byte{10, 11, 12, 1},
|
||||
Addr4: netip.MustParseAddr("10.11.12.1"),
|
||||
Port: 51820,
|
||||
Relay: true,
|
||||
WGPubKey: make([]byte, 32),
|
||||
SignPubKey: make([]byte, 32),
|
||||
WGPubKey: mustKey(t),
|
||||
SignPubKey: sign1,
|
||||
},
|
||||
{
|
||||
PeerIP: 10,
|
||||
Version: 3,
|
||||
Name: "laptop",
|
||||
Addr4: []byte{10, 11, 12, 10},
|
||||
Port: 51820,
|
||||
WGPubKey: []byte("0123456789abcdef0123456789abcdef"),
|
||||
SignPubKey: []byte("fedcba9876543210fedcba9876543210"),
|
||||
PeerIP: 10,
|
||||
Version: 3,
|
||||
Name: "laptop",
|
||||
Addr4: netip.MustParseAddr("10.11.12.10"),
|
||||
Port: 51820,
|
||||
WGPubKey: mustKey(t),
|
||||
},
|
||||
}}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"vppn/m"
|
||||
"vppn/peer/wginterface"
|
||||
)
|
||||
|
||||
@@ -57,7 +58,7 @@ func New(
|
||||
}
|
||||
|
||||
pingCh := make(chan PingEvent)
|
||||
hubAddCh := make(chan HubPeer)
|
||||
hubAddCh := make(chan m.Peer)
|
||||
hubRemoveCh := make(chan wgtypes.Key)
|
||||
multicastCh := make(chan MulticastEvent)
|
||||
|
||||
|
||||
@@ -8,33 +8,31 @@ import (
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"vppn/m"
|
||||
"vppn/peer/control"
|
||||
)
|
||||
|
||||
func (a *App) onAddPeer(p HubPeer) {
|
||||
a.onRemovePeer(p.PubKey)
|
||||
func (a *App) onAddPeer(p m.Peer) {
|
||||
a.onRemovePeer(p.WGPubKey)
|
||||
|
||||
octets := a.vpnNet.Addr().As4()
|
||||
octets[3] = p.PeerIP
|
||||
vpnIP := netip.AddrFrom4(octets)
|
||||
|
||||
peer := &Peer{
|
||||
wgPeer: wgtypes.Peer{PublicKey: p.PubKey},
|
||||
VPNIP: p.VPNIP,
|
||||
wgPeer: wgtypes.Peer{PublicKey: p.WGPubKey},
|
||||
VPNIP: vpnIP,
|
||||
Name: p.Name,
|
||||
IsRelay: p.IsRelay,
|
||||
IsPublic: p.IsPublic,
|
||||
Endpoint4: p.EndpointV4,
|
||||
Endpoint6: p.EndpointV6,
|
||||
IsRelay: p.Relay,
|
||||
IsPublic: p.IsPublic(),
|
||||
Endpoint4: p.Endpoint4(),
|
||||
Endpoint6: p.Endpoint6(),
|
||||
RTT: time.Duration(math.MaxInt64) * time.Nanosecond,
|
||||
Role: roleFor(a.isPublic, a.vpnIP, p),
|
||||
Role: roleFor(a.isPublic, a.vpnIP, p.IsPublic(), vpnIP),
|
||||
SignPubKey: p.SignPubKey,
|
||||
}
|
||||
|
||||
endpoint := peer.PreferredEndpoint()
|
||||
if peer.IsPublic && !endpoint.IsValid() {
|
||||
// The peer is misconfigured.
|
||||
// TODO: Log here.
|
||||
return
|
||||
}
|
||||
|
||||
a.peersByKey[p.PubKey] = peer
|
||||
a.peersByKey[p.WGPubKey] = peer
|
||||
a.peersByIP[peer.VPNIP] = peer
|
||||
defer a.updateHosts()
|
||||
|
||||
@@ -50,7 +48,7 @@ func (a *App) onAddPeer(p HubPeer) {
|
||||
return
|
||||
}
|
||||
|
||||
a.devAddDirect(peer, endpoint)
|
||||
a.devAddDirect(peer, peer.PreferredEndpoint())
|
||||
}
|
||||
|
||||
func (a *App) onRemovePeer(key wgtypes.Key) {
|
||||
@@ -104,12 +102,12 @@ func preferredEndpoint(v4, v6 netip.AddrPort) netip.AddrPort {
|
||||
return v6
|
||||
}
|
||||
|
||||
func roleFor(selfIsPublic bool, selfIP netip.Addr, p HubPeer) control.Role {
|
||||
if !selfIsPublic && p.IsPublic {
|
||||
func roleFor(selfIsPublic bool, selfIP netip.Addr, peerIsPublic bool, peerVPNIP netip.Addr) control.Role {
|
||||
if !selfIsPublic && peerIsPublic {
|
||||
return control.Client
|
||||
}
|
||||
if selfIsPublic && !p.IsPublic {
|
||||
if selfIsPublic && !peerIsPublic {
|
||||
return control.Server
|
||||
}
|
||||
return control.RoleFor(selfIP, p.VPNIP)
|
||||
return control.RoleFor(selfIP, peerVPNIP)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"vppn/m"
|
||||
)
|
||||
|
||||
func mustKey(t *testing.T) wgtypes.Key {
|
||||
@@ -25,13 +27,13 @@ func TestOnAddPeer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
setup func(a *App, key wgtypes.Key)
|
||||
peer func(key wgtypes.Key) HubPeer
|
||||
peer func(key wgtypes.Key) m.Peer
|
||||
check func(t *testing.T, a *App, dev *fakeWGDevice, key wgtypes.Key)
|
||||
}{
|
||||
{
|
||||
name: "non-public peer registered in WG via AddPeer",
|
||||
peer: func(k wgtypes.Key) HubPeer {
|
||||
return HubPeer{PubKey: k, VPNIP: peerVPNIP}
|
||||
peer: func(k wgtypes.Key) m.Peer {
|
||||
return m.Peer{WGPubKey: k, PeerIP: 2}
|
||||
},
|
||||
check: func(t *testing.T, a *App, dev *fakeWGDevice, key wgtypes.Key) {
|
||||
p := a.peersByKey[key]
|
||||
@@ -49,8 +51,8 @@ func TestOnAddPeer(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "public peer with endpoint registered via AddDirect",
|
||||
peer: func(k wgtypes.Key) HubPeer {
|
||||
return HubPeer{PubKey: k, VPNIP: peerVPNIP, IsPublic: true, EndpointV4: ep1}
|
||||
peer: func(k wgtypes.Key) m.Peer {
|
||||
return m.Peer{WGPubKey: k, PeerIP: 2, Addr4: ep1.Addr(), Port: ep1.Port()}
|
||||
},
|
||||
check: func(t *testing.T, a *App, dev *fakeWGDevice, key wgtypes.Key) {
|
||||
p := a.peersByKey[key]
|
||||
@@ -60,28 +62,13 @@ func TestOnAddPeer(t *testing.T) {
|
||||
dev.AssertAddDirect(t, 0, p.PubKey(), ep1, p.VPNIP)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "public peer with no endpoint is dropped",
|
||||
peer: func(k wgtypes.Key) HubPeer {
|
||||
return HubPeer{PubKey: k, VPNIP: peerVPNIP, IsPublic: true}
|
||||
},
|
||||
check: func(t *testing.T, a *App, dev *fakeWGDevice, key wgtypes.Key) {
|
||||
if a.peersByKey[key] != nil {
|
||||
t.Fatal("peer should not be in peersByKey")
|
||||
}
|
||||
if a.peersByIP[peerVPNIP] != nil {
|
||||
t.Fatal("peer should not be in peersByIP")
|
||||
}
|
||||
dev.AssertNoCalls(t)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "re-add removes old WG entry before adding new one",
|
||||
setup: func(a *App, key wgtypes.Key) {
|
||||
a.onAddPeer(HubPeer{PubKey: key, VPNIP: peerVPNIP, IsPublic: true, EndpointV4: ep1})
|
||||
a.onAddPeer(m.Peer{WGPubKey: key, PeerIP: 2, Addr4: ep1.Addr(), Port: ep1.Port()})
|
||||
},
|
||||
peer: func(k wgtypes.Key) HubPeer {
|
||||
return HubPeer{PubKey: k, VPNIP: peerVPNIP, IsPublic: true, EndpointV4: ep2}
|
||||
peer: func(k wgtypes.Key) m.Peer {
|
||||
return m.Peer{WGPubKey: k, PeerIP: 2, Addr4: ep2.Addr(), Port: ep2.Port()}
|
||||
},
|
||||
check: func(t *testing.T, a *App, dev *fakeWGDevice, key wgtypes.Key) {
|
||||
if len(dev.Calls) != 2 {
|
||||
@@ -135,7 +122,7 @@ func TestOnRemovePeer(t *testing.T) {
|
||||
name: "StateRelayed peer removed from maps with RemovePeer",
|
||||
setup: func(t *testing.T, a *App) wgtypes.Key {
|
||||
key := mustKey(t)
|
||||
a.onAddPeer(HubPeer{PubKey: key, VPNIP: netip.MustParseAddr("10.0.0.2")})
|
||||
a.onAddPeer(m.Peer{WGPubKey: key, PeerIP: 2})
|
||||
return key
|
||||
},
|
||||
check: func(t *testing.T, a *App, dev *fakeWGDevice) {
|
||||
@@ -152,7 +139,7 @@ func TestOnRemovePeer(t *testing.T) {
|
||||
name: "StateDirect peer removed from maps with RemovePeer",
|
||||
setup: func(t *testing.T, a *App) wgtypes.Key {
|
||||
key := mustKey(t)
|
||||
a.onAddPeer(HubPeer{PubKey: key, VPNIP: netip.MustParseAddr("10.0.0.2"), IsPublic: true, EndpointV4: ep1})
|
||||
a.onAddPeer(m.Peer{WGPubKey: key, PeerIP: 2, Addr4: ep1.Addr(), Port: ep1.Port()})
|
||||
return key
|
||||
},
|
||||
check: func(t *testing.T, a *App, dev *fakeWGDevice) {
|
||||
|
||||
Reference in New Issue
Block a user