This commit is contained in:
jdl
2026-06-05 19:43:27 +02:00
parent 8b2c9709fc
commit b972784d90
43 changed files with 945 additions and 3066 deletions

View File

@@ -3,11 +3,8 @@ package peer
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"math"
"net"
"net/http"
"net/netip"
"net/url"
@@ -15,14 +12,17 @@ import (
"vppn/m"
"git.crumpington.com/lib/go/flock"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type peerMain struct {
Globals
ifReader *IFReader
connReader *ConnReader
hubPoller *HubPoller
lockFile *os.File
netName string
holePunch *HolePunch
controlServer *ControlServer
endpointReporter *EndpointReporter // non-nil on relay peers only
hubPoller *HubPoller
lockFile *os.File
}
func newPeerMain(args mainArgs) *peerMain {
@@ -59,89 +59,104 @@ func newPeerMain(args mainArgs) *peerMain {
log.Fatalf("Failed to load network state: %v", err)
}
startupCount, err := loadStartupCount(args.NetName)
wgPrivKey, err := wgtypes.ParseKey(config.WGPrivKey)
if err != nil {
if !os.IsNotExist(err) {
log.Fatalf("Failed to load startup count: %v", err)
}
}
if startupCount.Count == math.MaxUint16 {
log.Fatalf("Startup counter overflow.")
}
startupCount.Count += 1
if err := storeStartupCount(args.NetName, startupCount); err != nil {
log.Fatalf("Failed to write startup count: %v", err)
}
iface, err := openInterface(config.Network, config.LocalPeerIP, args.NetName)
if err != nil {
log.Fatalf("Failed to open interface: %v", err)
log.Fatalf("Failed to parse WireGuard private key: %v", err)
}
localPeer := state.Peers[config.LocalPeerIP]
var listenPort int
if localPeer != nil {
listenPort = int(localPeer.Port1)
}
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", localPeer.Port))
vpnIP := netip.AddrFrom4([4]byte{
config.Network[0],
config.Network[1],
config.Network[2],
config.LocalPeerIP,
})
wgClient, err := createWGDevice(args.NetName, wgPrivKey, listenPort, vpnIP, config.Network)
if err != nil {
log.Fatalf("Failed to resolve UDP address: %v", err)
log.Fatalf("Failed to create WireGuard device: %v", err)
}
logf("Listening on %v...", myAddr)
conn, err := net.ListenUDP("udp", myAddr)
for _, p := range state.Peers {
if p == nil || !p.Relay || p.PeerIP == config.LocalPeerIP {
continue
}
if len(p.WGPubKey) != wgtypes.KeyLen || len(p.PublicIP1) == 0 || p.Port1 == 0 {
continue
}
relayPubKey, err := wgtypes.NewKey(p.WGPubKey)
if err != nil {
logf("Invalid relay WG key: %v", err)
continue
}
relayIP, ok := netip.AddrFromSlice(p.PublicIP1)
if !ok {
continue
}
relayEndpoint := netip.AddrPortFrom(relayIP.Unmap(), p.Port1)
if err := applyBaseConfig(wgClient, args.NetName, relayPubKey, relayEndpoint, config.Network); err != nil {
logf("Failed to apply relay base config: %v", err)
}
break
}
g := NewGlobals(config, netip.AddrPort{})
g.WGPrivKey = wgPrivKey
g.WGClient = wgClient
g.WGDevName = args.NetName
holePunch := NewHolePunch(g)
controlServer, err := NewControlServer(g, holePunch, args.NetName)
if err != nil {
log.Fatalf("Failed to open UDP port: %v", err)
log.Fatalf("Failed to create control server: %v", err)
}
conn.SetReadBuffer(1024 * 1024 * 8)
conn.SetWriteBuffer(1024 * 1024 * 8)
var localAddr netip.AddrPort
ip, localAddrValid := netip.AddrFromSlice(localPeer.PublicIP)
if localAddrValid {
localAddr = netip.AddrPortFrom(ip, localPeer.Port)
var endpointReporter *EndpointReporter
if localPeer != nil && localPeer.Relay {
if err := enableForwarding(args.NetName); err != nil {
log.Fatalf("Failed to enable IP forwarding: %v", err)
}
endpointReporter = NewEndpointReporter(g, controlServer, args.NetName)
}
g := NewGlobals(config, startupCount, localAddr, conn, iface)
hubPoller, err := NewHubPoller(g, args.NetName, args.HubAddress, args.APIKey)
hubPoller, err := NewHubPoller(g, holePunch, args.NetName, args.HubAddress, args.APIKey)
if err != nil {
log.Fatalf("Failed to create hub poller: %v", err)
}
// Start status server.
go runStatusServer(g, statusSocketPath(args.NetName))
return &peerMain{
Globals: g,
ifReader: NewIFReader(g),
connReader: NewConnReader(g, conn),
hubPoller: hubPoller,
lockFile: lockFile,
Globals: g,
netName: args.NetName,
holePunch: holePunch,
controlServer: controlServer,
endpointReporter: endpointReporter,
hubPoller: hubPoller,
lockFile: lockFile,
}
}
func (p *peerMain) Run() {
for i := range p.RemotePeers {
remote := p.RemotePeers[i].Load()
go newRemoteFSM(remote).Run()
go p.controlServer.Run()
if p.endpointReporter != nil {
go p.endpointReporter.Run()
}
go p.ifReader.Run()
go p.connReader.Run()
if !p.LocalAddrValid {
go RunMCWriter(p.LocalPeerIP, p.PrivSignKey)
go RunMCReader(p.Globals)
}
go RunMCWriter(p.Globals)
go RunMCReader(p.Globals, p.holePunch, p.netName)
go p.hubPoller.Run()
select {}
}
func initPeerWithHub(args mainArgs) {
keys := generateKeys()
privKey := generateWGKey()
pubKey := privKey.PublicKey()
initURL, err := url.Parse(args.HubAddress)
if err != nil {
@@ -150,8 +165,7 @@ func initPeerWithHub(args mainArgs) {
initURL.Path = "/peer/init/"
initArgs := m.PeerInitArgs{
EncPubKey: keys.PubKey,
PubSignKey: keys.PubSignKey,
WGPubKey: pubKey[:],
}
buf := &bytes.Buffer{}
@@ -176,18 +190,23 @@ func initPeerWithHub(args mainArgs) {
log.Fatalf("Failed to read response body: %v", err)
}
if resp.StatusCode == http.StatusConflict {
log.Fatalf("WireGuard key already registered (HTTP 409). Delete and re-create the peer to re-register.")
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("Hub returned unexpected status %d: %s", resp.StatusCode, data)
}
initResp := m.PeerInitResp{}
if err := json.Unmarshal(data, &initResp); err != nil {
log.Fatalf("Failed to parse configuration: %v\n%s", err, data)
}
config := LocalConfig{}
config.LocalPeerIP = initResp.PeerIP
config.Network = initResp.Network
config.PubKey = keys.PubKey
config.PrivKey = keys.PrivKey
config.PubSignKey = keys.PubSignKey
config.PrivSignKey = keys.PrivSignKey
config := LocalConfig{
LocalPeerIP: initResp.PeerIP,
Network: initResp.Network,
WGPrivKey: privKey.String(),
}
if err := storeNetworkState(args.NetName, initResp.NetworkState); err != nil {
log.Fatalf("Failed to store network state: %v", err)