Export wireguard logger

This commit is contained in:
Owen
2025-11-18 14:53:12 -05:00
parent 75e666c396
commit 025c94e586
6 changed files with 243 additions and 155 deletions

View File

@@ -35,8 +35,9 @@ type WgConfig struct {
} }
type Target struct { type Target struct {
CIDR string `json:"cidr"` SourcePrefix string `json:"sourcePrefix"`
PortRange []PortRange `json:"portRange,omitempty"` DestPrefix string `json:"destPrefix"`
PortRange []PortRange `json:"portRange,omitempty"`
} }
type PortRange struct { type PortRange struct {
@@ -332,9 +333,9 @@ func (s *WireGuardService) handleConfig(msg websocket.WSMessage) {
logger.Error("Failed to ensure WireGuard peers: %v", err) logger.Error("Failed to ensure WireGuard peers: %v", err)
} }
// if err := s.ensureTargets(config.Targets); err != nil { if err := s.ensureTargets(config.Targets); err != nil {
// logger.Error("Failed to ensure WireGuard targets: %v", err) logger.Error("Failed to ensure WireGuard targets: %v", err)
// } }
} }
func (s *WireGuardService) ensureWireguardInterface(wgconfig WgConfig) error { func (s *WireGuardService) ensureWireguardInterface(wgconfig WgConfig) error {
@@ -460,15 +461,15 @@ func (s *WireGuardService) ensureTargets(targets []Target) error {
return fmt.Errorf("netstack not initialized") return fmt.Errorf("netstack not initialized")
} }
// handler.AddSubnetRule(subnet2, []PortRange{
// {Min: 12000, Max: 12001},
// {Min: 8000, Max: 8000},
// })
for _, target := range targets { for _, target := range targets {
prefix, err := netip.ParsePrefix(target.CIDR) sourcePrefix, err := netip.ParsePrefix(target.SourcePrefix)
if err != nil { if err != nil {
return fmt.Errorf("invalid CIDR %s: %v", target.CIDR, err) return fmt.Errorf("invalid CIDR %s: %v", target.SourcePrefix, err)
}
destPrefix, err := netip.ParsePrefix(target.DestPrefix)
if err != nil {
return fmt.Errorf("invalid CIDR %s: %v", target.DestPrefix, err)
} }
var portRanges []netstack2.PortRange var portRanges []netstack2.PortRange
@@ -479,9 +480,9 @@ func (s *WireGuardService) ensureTargets(targets []Target) error {
}) })
} }
s.tnet.AddProxySubnetRule(prefix, portRanges) s.tnet.AddProxySubnetRule(sourcePrefix, destPrefix, portRanges)
logger.Info("Added target subnet %s with port ranges: %v", target.CIDR, target.PortRange) logger.Info("Added target subnet %s with port ranges: %v", target.SourcePrefix, target.PortRange)
} }
return nil return nil
@@ -830,7 +831,6 @@ func (s *WireGuardService) reportPeerBandwidth() error {
// filterReadOnlyFields removes read-only fields from WireGuard IPC configuration // filterReadOnlyFields removes read-only fields from WireGuard IPC configuration
func (s *WireGuardService) handleAddTarget(msg websocket.WSMessage) { func (s *WireGuardService) handleAddTarget(msg websocket.WSMessage) {
logger.Debug("Received message: %v", msg.Data) logger.Debug("Received message: %v", msg.Data)
var target Target
jsonData, err := json.Marshal(msg.Data) jsonData, err := json.Marshal(msg.Data)
if err != nil { if err != nil {
@@ -838,33 +838,86 @@ func (s *WireGuardService) handleAddTarget(msg websocket.WSMessage) {
return return
} }
if err := json.Unmarshal(jsonData, &target); err != nil {
logger.Info("Error unmarshaling target data: %v", err)
return
}
if s.tnet == nil { if s.tnet == nil {
logger.Info("Netstack not initialized") logger.Info("Netstack not initialized")
return return
} }
prefix, err := netip.ParsePrefix(target.CIDR) // Try to unmarshal as array first
if err != nil { var targets []Target
logger.Info("Invalid CIDR %s: %v", target.CIDR, err) if err := json.Unmarshal(jsonData, &targets); err != nil {
logger.Warn("Error unmarshaling target data: %v", err)
return return
} }
var portRanges []netstack2.PortRange // Process all targets
for _, pr := range target.PortRange { for _, target := range targets {
portRanges = append(portRanges, netstack2.PortRange{ sourcePrefix, err := netip.ParsePrefix(target.SourcePrefix)
Min: pr.Min, if err != nil {
Max: pr.Max, logger.Info("Invalid CIDR %s: %v", target.SourcePrefix, err)
}) continue
}
destPrefix, err := netip.ParsePrefix(target.DestPrefix)
if err != nil {
logger.Info("Invalid CIDR %s: %v", target.DestPrefix, err)
continue
}
var portRanges []netstack2.PortRange
for _, pr := range target.PortRange {
portRanges = append(portRanges, netstack2.PortRange{
Min: pr.Min,
Max: pr.Max,
})
}
s.tnet.AddProxySubnetRule(sourcePrefix, destPrefix, portRanges)
logger.Info("Added target subnet %s with port ranges: %v", target.SourcePrefix, target.PortRange)
}
}
// filterReadOnlyFields removes read-only fields from WireGuard IPC configuration
func (s *WireGuardService) handleRemoveTarget(msg websocket.WSMessage) {
logger.Debug("Received message: %v", msg.Data)
jsonData, err := json.Marshal(msg.Data)
if err != nil {
logger.Info("Error marshaling data: %v", err)
return
} }
s.tnet.AddProxySubnetRule(prefix, portRanges) if s.tnet == nil {
logger.Info("Netstack not initialized")
return
}
logger.Info("Added target subnet %s with port ranges: %v", target.CIDR, target.PortRange) // Try to unmarshal as array first
var targets []Target
if err := json.Unmarshal(jsonData, &targets); err != nil {
logger.Warn("Error unmarshaling target data: %v", err)
return
}
// Process all targets
for _, target := range targets {
sourcePrefix, err := netip.ParsePrefix(target.SourcePrefix)
if err != nil {
logger.Info("Invalid CIDR %s: %v", target.SourcePrefix, err)
continue
}
destPrefix, err := netip.ParsePrefix(target.DestPrefix)
if err != nil {
logger.Info("Invalid CIDR %s: %v", target.DestPrefix, err)
continue
}
s.tnet.RemoveProxySubnetRule(sourcePrefix, destPrefix)
logger.Info("Removed target subnet %s", target.SourcePrefix)
}
} }
func (s *WireGuardService) handleUpdateTarget(msg websocket.WSMessage) { func (s *WireGuardService) handleUpdateTarget(msg websocket.WSMessage) {
@@ -872,8 +925,8 @@ func (s *WireGuardService) handleUpdateTarget(msg websocket.WSMessage) {
// you are going to get a oldTarget and a newTarget in the message // you are going to get a oldTarget and a newTarget in the message
type UpdateTargetRequest struct { type UpdateTargetRequest struct {
OldTarget Target `json:"oldTarget"` OldTargets []Target `json:"oldTargets"`
NewTarget Target `json:"newTarget"` NewTargets []Target `json:"newTargets"`
} }
jsonData, err := json.Marshal(msg.Data) jsonData, err := json.Marshal(msg.Data)
@@ -882,78 +935,59 @@ func (s *WireGuardService) handleUpdateTarget(msg websocket.WSMessage) {
return return
} }
var request UpdateTargetRequest
if err := json.Unmarshal(jsonData, &request); err != nil {
logger.Info("Error unmarshaling data: %v", err)
return
}
if s.tnet == nil { if s.tnet == nil {
logger.Info("Netstack not initialized") logger.Info("Netstack not initialized")
return return
} }
prefix, err := netip.ParsePrefix(request.OldTarget.CIDR) // Try to unmarshal as array first
if err != nil { var requests UpdateTargetRequest
logger.Info("Invalid CIDR %s: %v", request.OldTarget.CIDR, err) if err := json.Unmarshal(jsonData, &requests); err != nil {
logger.Warn("Error unmarshaling target data: %v", err)
return return
} }
s.tnet.RemoveProxySubnetRule(prefix) // Process all update requests
for _, target := range requests.OldTargets {
sourcePrefix, err := netip.ParsePrefix(target.SourcePrefix)
if err != nil {
logger.Info("Invalid CIDR %s: %v", target.SourcePrefix, err)
continue
}
// Now add the new target destPrefix, err := netip.ParsePrefix(target.DestPrefix)
newPrefix, err := netip.ParsePrefix(request.NewTarget.CIDR) if err != nil {
if err != nil { logger.Info("Invalid CIDR %s: %v", target.DestPrefix, err)
logger.Info("Invalid CIDR %s: %v", request.NewTarget.CIDR, err) continue
return }
s.tnet.RemoveProxySubnetRule(sourcePrefix, destPrefix)
} }
var portRanges []netstack2.PortRange for _, target := range requests.NewTargets {
for _, pr := range request.NewTarget.PortRange { // Now add the new target
portRanges = append(portRanges, netstack2.PortRange{ sourcePrefix, err := netip.ParsePrefix(target.SourcePrefix)
Min: pr.Min, if err != nil {
Max: pr.Max, logger.Info("Invalid CIDR %s: %v", target.SourcePrefix, err)
}) continue
}
destPrefix, err := netip.ParsePrefix(target.DestPrefix)
if err != nil {
logger.Info("Invalid CIDR %s: %v", target.DestPrefix, err)
continue
}
var portRanges []netstack2.PortRange
for _, pr := range target.PortRange {
portRanges = append(portRanges, netstack2.PortRange{
Min: pr.Min,
Max: pr.Max,
})
}
s.tnet.AddProxySubnetRule(sourcePrefix, destPrefix, portRanges)
} }
s.tnet.AddProxySubnetRule(newPrefix, portRanges)
logger.Info("Updated target subnet from %s to %s", request.OldTarget.CIDR, request.NewTarget.CIDR)
}
func (s *WireGuardService) handleRemoveTarget(msg websocket.WSMessage) {
logger.Debug("Received message: %v", msg.Data)
type RemoveTargetRequest struct {
CIDR string `json:"cidr"`
}
jsonData, err := json.Marshal(msg.Data)
if err != nil {
logger.Info("Error marshaling data: %v", err)
return
}
var request RemoveTargetRequest
if err := json.Unmarshal(jsonData, &request); err != nil {
logger.Info("Error unmarshaling data: %v", err)
return
}
if s.tnet == nil {
logger.Info("Netstack not initialized")
return
}
prefix, err := netip.ParsePrefix(request.CIDR)
if err != nil {
logger.Info("Invalid CIDR %s: %v", request.CIDR, err)
return
}
s.tnet.RemoveProxySubnetRule(prefix)
logger.Info("Removed target subnet %s", request.CIDR)
} }
// filterReadOnlyFields removes read-only fields from WireGuard IPC configuration // filterReadOnlyFields removes read-only fields from WireGuard IPC configuration

View File

@@ -173,10 +173,10 @@ func (m *Manager) runMultipleExitNodes(exitNodes []ExitNode) {
} }
} }
ticker := time.NewTicker(250 * time.Millisecond) ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() defer ticker.Stop()
timeout := time.NewTimer(15 * time.Second) timeout := time.NewTimer(5 * time.Second)
defer timeout.Stop() defer timeout.Stop()
for { for {
@@ -226,10 +226,10 @@ func (m *Manager) runSingleEndpoint(endpoint, serverPubKey string) {
logger.Warn("Failed to send initial hole punch: %v", err) logger.Warn("Failed to send initial hole punch: %v", err)
} }
ticker := time.NewTicker(250 * time.Millisecond) ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() defer ticker.Stop()
timeout := time.NewTimer(15 * time.Second) timeout := time.NewTimer(5 * time.Second)
defer timeout.Stop() defer timeout.Stop()
for { for {

View File

@@ -127,3 +127,22 @@ func Fatal(format string, args ...interface{}) {
func SetOutput(output *os.File) { func SetOutput(output *os.File) {
GetLogger().SetOutput(output) GetLogger().SetOutput(output)
} }
// WireGuardLogger is a wrapper type that matches WireGuard's Logger interface
type WireGuardLogger struct {
Verbosef func(format string, args ...any)
Errorf func(format string, args ...any)
}
// GetWireGuardLogger returns a WireGuard-compatible logger that writes to the newt logger
// The prepend string is added as a prefix to all log messages
func (l *Logger) GetWireGuardLogger(prepend string) *WireGuardLogger {
return &WireGuardLogger{
Verbosef: func(format string, args ...any) {
l.Debug(prepend+format, args...)
},
Errorf: func(format string, args ...any) {
l.Error(prepend+format, args...)
},
}
}

View File

@@ -368,7 +368,7 @@ func main() {
tlsClientCAs = append(tlsClientCAs, tlsClientCAsFlag...) tlsClientCAs = append(tlsClientCAs, tlsClientCAsFlag...)
} }
logger.Init() logger.Init(nil)
loggerLevel := util.ParseLogLevel(logLevel) loggerLevel := util.ParseLogLevel(logLevel)
logger.GetLogger().SetLevel(loggerLevel) logger.GetLogger().SetLevel(loggerLevel)

View File

@@ -23,68 +23,95 @@ type PortRange struct {
Max uint16 Max uint16
} }
// SubnetRule represents a subnet with optional port restrictions // SubnetRule represents a subnet with optional port restrictions and source address
type SubnetRule struct { type SubnetRule struct {
Prefix netip.Prefix SourcePrefix netip.Prefix // Source IP prefix (who is sending)
PortRanges []PortRange // empty slice means all ports allowed DestPrefix netip.Prefix // Destination IP prefix (where it's going)
PortRanges []PortRange // empty slice means all ports allowed
} }
// SubnetLookup provides fast IP subnet and port matching // ruleKey is used as a map key for fast O(1) lookups
type ruleKey struct {
sourcePrefix string
destPrefix string
}
// SubnetLookup provides fast IP subnet and port matching with O(1) lookup performance
type SubnetLookup struct { type SubnetLookup struct {
mu sync.RWMutex mu sync.RWMutex
rules []SubnetRule rules map[ruleKey]*SubnetRule // Map for O(1) lookups by prefix combination
} }
// NewSubnetLookup creates a new subnet lookup table // NewSubnetLookup creates a new subnet lookup table
func NewSubnetLookup() *SubnetLookup { func NewSubnetLookup() *SubnetLookup {
return &SubnetLookup{ return &SubnetLookup{
rules: make([]SubnetRule, 0), rules: make(map[ruleKey]*SubnetRule),
} }
} }
// AddSubnet adds a subnet to the lookup table with optional port restrictions // AddSubnet adds a subnet rule with source and destination prefixes and optional port restrictions
// If portRanges is nil or empty, all ports are allowed for this subnet // If portRanges is nil or empty, all ports are allowed for this subnet
func (sl *SubnetLookup) AddSubnet(prefix netip.Prefix, portRanges []PortRange) { func (sl *SubnetLookup) AddSubnet(sourcePrefix, destPrefix netip.Prefix, portRanges []PortRange) {
sl.mu.Lock() sl.mu.Lock()
defer sl.mu.Unlock() defer sl.mu.Unlock()
sl.rules = append(sl.rules, SubnetRule{ key := ruleKey{
Prefix: prefix, sourcePrefix: sourcePrefix.String(),
PortRanges: portRanges, destPrefix: destPrefix.String(),
}) }
}
// RemoveSubnet removes a subnet from the lookup table sl.rules[key] = &SubnetRule{
func (sl *SubnetLookup) RemoveSubnet(prefix netip.Prefix) { SourcePrefix: sourcePrefix,
sl.mu.Lock() DestPrefix: destPrefix,
defer sl.mu.Unlock() PortRanges: portRanges,
for i, rule := range sl.rules {
if rule.Prefix == prefix {
sl.rules = append(sl.rules[:i], sl.rules[i+1:]...)
return
}
} }
} }
// Match checks if an IP and port match any subnet rule // RemoveSubnet removes a subnet rule from the lookup table
// Returns true if the IP is in a matching subnet AND the port is in an allowed range func (sl *SubnetLookup) RemoveSubnet(sourcePrefix, destPrefix netip.Prefix) {
func (sl *SubnetLookup) Match(ip netip.Addr, port uint16) bool { sl.mu.Lock()
defer sl.mu.Unlock()
key := ruleKey{
sourcePrefix: sourcePrefix.String(),
destPrefix: destPrefix.String(),
}
delete(sl.rules, key)
}
// Match checks if a source IP, destination IP, and port match any subnet rule
// Returns true if BOTH:
// - The source IP is in the rule's source prefix
// - The destination IP is in the rule's destination prefix
// - The port is in an allowed range (or no port restrictions exist)
//
// This implementation uses O(n) iteration but checks exact prefix matches first for common cases
func (sl *SubnetLookup) Match(srcIP, dstIP netip.Addr, port uint16) bool {
sl.mu.RLock() sl.mu.RLock()
defer sl.mu.RUnlock() defer sl.mu.RUnlock()
// Iterate through all rules to find matching source and destination prefixes
// This is O(n) but necessary since we need to check prefix containment, not exact match
for _, rule := range sl.rules { for _, rule := range sl.rules {
if rule.Prefix.Contains(ip) { // Check if source and destination IPs match their respective prefixes
// If no port ranges specified, all ports are allowed if !rule.SourcePrefix.Contains(srcIP) {
if len(rule.PortRanges) == 0 { continue
return true }
} if !rule.DestPrefix.Contains(dstIP) {
continue
}
// Check if port is in any of the allowed ranges // Both IPs match - now check port restrictions
for _, pr := range rule.PortRanges { // If no port ranges specified, all ports are allowed
if port >= pr.Min && port <= pr.Max { if len(rule.PortRanges) == 0 {
return true return true
} }
// Check if port is in any of the allowed ranges
for _, pr := range rule.PortRanges {
if port >= pr.Min && port <= pr.Max {
return true
} }
} }
} }
@@ -150,37 +177,42 @@ func NewProxyHandler(options ProxyHandlerOptions) (*ProxyHandler, error) {
} }
} }
// // Example 1: Add a subnet with no port restrictions (all ports allowed) // // Example 1: Add a rule with no port restrictions (all ports allowed)
// // This accepts all traffic to 10.20.20.0/24 // // This accepts all traffic FROM 10.0.0.0/24 TO 10.20.20.0/24
// subnet1 := netip.MustParsePrefix("10.20.20.0/24") // sourceSubnet := netip.MustParsePrefix("10.0.0.0/24")
// handler.AddSubnetRule(subnet1, nil) // destSubnet := netip.MustParsePrefix("10.20.20.0/24")
// handler.AddSubnetRule(sourceSubnet, destSubnet, nil)
// // Example 2: Add a subnet with specific port ranges // // Example 2: Add a rule with specific port ranges
// // This accepts traffic to 192.168.1.0/24 only on ports 80, 443, and 8000-9000 // // This accepts traffic FROM 10.0.0.5/32 TO 10.20.21.21/32 only on ports 80, 443, and 8000-9000
// subnet2 := netip.MustParsePrefix("10.20.21.21/32") // sourceIP := netip.MustParsePrefix("10.0.0.5/32")
// handler.AddSubnetRule(subnet2, []PortRange{ // destIP := netip.MustParsePrefix("10.20.21.21/32")
// {Min: 12000, Max: 12001}, // handler.AddSubnetRule(sourceIP, destIP, []PortRange{
// {Min: 8000, Max: 8000}, // {Min: 80, Max: 80},
// {Min: 443, Max: 443},
// {Min: 8000, Max: 9000},
// }) // })
return handler, nil return handler, nil
} }
// AddSubnetRule adds a subnet with optional port restrictions to the proxy handler // AddSubnetRule adds a subnet with optional port restrictions to the proxy handler
// sourcePrefix: The IP prefix of the peer sending the data
// destPrefix: The IP prefix of the destination
// If portRanges is nil or empty, all ports are allowed for this subnet // If portRanges is nil or empty, all ports are allowed for this subnet
func (p *ProxyHandler) AddSubnetRule(prefix netip.Prefix, portRanges []PortRange) { func (p *ProxyHandler) AddSubnetRule(sourcePrefix, destPrefix netip.Prefix, portRanges []PortRange) {
if p == nil || !p.enabled { if p == nil || !p.enabled {
return return
} }
p.subnetLookup.AddSubnet(prefix, portRanges) p.subnetLookup.AddSubnet(sourcePrefix, destPrefix, portRanges)
} }
// RemoveSubnetRule removes a subnet from the proxy handler // RemoveSubnetRule removes a subnet from the proxy handler
func (p *ProxyHandler) RemoveSubnetRule(prefix netip.Prefix) { func (p *ProxyHandler) RemoveSubnetRule(sourcePrefix, destPrefix netip.Prefix) {
if p == nil || !p.enabled { if p == nil || !p.enabled {
return return
} }
p.subnetLookup.RemoveSubnet(prefix) p.subnetLookup.RemoveSubnet(sourcePrefix, destPrefix)
} }
// Initialize sets up the promiscuous NIC with the netTun's notification system // Initialize sets up the promiscuous NIC with the netTun's notification system
@@ -239,11 +271,14 @@ func (p *ProxyHandler) HandleIncomingPacket(packet []byte) bool {
// Parse IPv4 header // Parse IPv4 header
ipv4Header := header.IPv4(packet) ipv4Header := header.IPv4(packet)
srcIP := ipv4Header.SourceAddress()
dstIP := ipv4Header.DestinationAddress() dstIP := ipv4Header.DestinationAddress()
// Convert gvisor tcpip.Address to netip.Addr // Convert gvisor tcpip.Address to netip.Addr
srcBytes := srcIP.As4()
srcAddr := netip.AddrFrom4(srcBytes)
dstBytes := dstIP.As4() dstBytes := dstIP.As4()
addr := netip.AddrFrom4(dstBytes) dstAddr := netip.AddrFrom4(dstBytes)
// Parse transport layer to get destination port // Parse transport layer to get destination port
var dstPort uint16 var dstPort uint16
@@ -269,8 +304,8 @@ func (p *ProxyHandler) HandleIncomingPacket(packet []byte) bool {
dstPort = 0 dstPort = 0
} }
// Check if the destination IP and port match any subnet rule // Check if the source IP, destination IP, and port match any subnet rule
if p.subnetLookup.Match(addr, dstPort) { if p.subnetLookup.Match(srcAddr, dstAddr, dstPort) {
// Inject into proxy stack // Inject into proxy stack
pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{ pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer.MakeWithData(packet), Payload: buffer.MakeWithData(packet),

View File

@@ -350,18 +350,18 @@ func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) {
// AddProxySubnetRule adds a subnet rule to the proxy handler // AddProxySubnetRule adds a subnet rule to the proxy handler
// If portRanges is nil or empty, all ports are allowed for this subnet // If portRanges is nil or empty, all ports are allowed for this subnet
func (net *Net) AddProxySubnetRule(prefix netip.Prefix, portRanges []PortRange) { func (net *Net) AddProxySubnetRule(sourcePrefix, destPrefix netip.Prefix, portRanges []PortRange) {
tun := (*netTun)(net) tun := (*netTun)(net)
if tun.proxyHandler != nil { if tun.proxyHandler != nil {
tun.proxyHandler.AddSubnetRule(prefix, portRanges) tun.proxyHandler.AddSubnetRule(sourcePrefix, destPrefix, portRanges)
} }
} }
// RemoveProxySubnetRule removes a subnet rule from the proxy handler // RemoveProxySubnetRule removes a subnet rule from the proxy handler
func (net *Net) RemoveProxySubnetRule(prefix netip.Prefix) { func (net *Net) RemoveProxySubnetRule(sourcePrefix, destPrefix netip.Prefix) {
tun := (*netTun)(net) tun := (*netTun)(net)
if tun.proxyHandler != nil { if tun.proxyHandler != nil {
tun.proxyHandler.RemoveSubnetRule(prefix) tun.proxyHandler.RemoveSubnetRule(sourcePrefix, destPrefix)
} }
} }