wgctl/daemon/wgctl-conntrack/resolver/peers.go
Nuno Duque Nunes d314ba376e feat: wgctl-conntrack Go daemon
- conntrack/event.go: TrafficEvent type
- conntrack/filter.go: WG subnet filter, IsExternal, ProtoName
- conntrack/subscriber.go: netlink conntrack DESTROY subscriber
- writer/log.go: JSON line writer with mutex
- resolver/peers.go: WG IP → peer name from conf files + endpoint index
- resolver/services.go: IP:port → service name from services.json
- config/config.go: reads wgctl.json, sensible defaults
- cmd/root.go: CLI flags
- main.go: wires everything together
- DESTROY events only: full byte/packet counts per connection
- filters to WireGuard subnet, marks external traffic
2026-05-28 02:51:27 +00:00

93 lines
No EOL
2 KiB
Go

package resolver
import (
"encoding/json"
"net"
"os"
"strings"
"sync"
"time"
)
// PeerResolver maps WireGuard peer IPs to peer names
type PeerResolver struct {
mu sync.RWMutex
ipToName map[string]string
wgDir string
}
func NewPeerResolver(wgDir string) *PeerResolver {
r := &PeerResolver{wgDir: wgDir, ipToName: make(map[string]string)}
r.reload()
go r.watchReload()
return r
}
func (r *PeerResolver) PeerForIP(ip net.IP) string {
r.mu.RLock()
defer r.mu.RUnlock()
return r.ipToName[ip.String()]
}
func (r *PeerResolver) reload() {
newMap := make(map[string]string)
// WireGuard IPs from conf files (10.1.x.x → peer name)
clientsDir := r.wgDir + "/clients"
entries, err := os.ReadDir(clientsDir)
if err == nil {
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".conf") {
continue
}
name := strings.TrimSuffix(entry.Name(), ".conf")
if ip := parseAddressFromConf(clientsDir + "/" + entry.Name()); ip != "" {
newMap[ip] = name
}
}
}
// External IPs from endpoint index (external IP → peer name)
indexFile := r.wgDir + "/.wgctl/data/peer-history/endpoint_index.json"
if data, err := os.ReadFile(indexFile); err == nil {
var index map[string]string
if json.Unmarshal(data, &index) == nil {
for ip, peer := range index {
newMap[ip] = peer
}
}
}
r.mu.Lock()
r.ipToName = newMap
r.mu.Unlock()
}
func (r *PeerResolver) watchReload() {
ticker := time.NewTicker(60 * time.Second)
defer ticker.Stop()
for range ticker.C {
r.reload()
}
}
func parseAddressFromConf(path string) string {
data, err := os.ReadFile(path)
if err != nil {
return ""
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "Address") {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
ip := strings.TrimSpace(parts[1])
if idx := strings.Index(ip, "/"); idx != -1 {
ip = ip[:idx]
}
return ip
}
}
}
return ""
}