OpenNHP/opennhp · error

relay source address

Error message

%s relay source address

What it means

HandleRelayForward validates the claimed real client address carried inside a relayed packet. validateRelaySourceAddr returns a reason (e.g. nil IP, disallowed private/reserved source) and the handler wraps it into this error, rejecting the forward from the relay. It prevents relays from spoofing client source addresses.

Solutions

  1. Check the relay is stamping the real public client IP into SourceAddr.Ip before forwarding
  2. If relays legitimately sit on private networks, enable the allowPrivateRelaySource option on the server
  3. Verify the relay software version matches the server's expected relay message format
  4. Inspect the warning log line 'rejecting %s from relay' for the exact reason string

Example fix

// before (relay on private net, rejected)
// server: allowPrivateRelaySource = false
// after
cfg.AllowPrivateRelaySource.Store(true) // only if relays are trusted and on private links
Defensive patterns

Strategy: validation

Validate before calling

ip := net.ParseIP(sourceAddrIp)
if ip == nil || (ip.IsPrivate() && !allowPrivateRelaySource) {
    return fmt.Errorf("relay source address %q will be rejected", sourceAddrIp)
}

Type guard

func isRelaySourceAllowed(ipStr string, allowPrivate bool) bool {
    ip := net.ParseIP(ipStr)
    if ip == nil { return false }
    if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() { return allowPrivate }
    return true
}

Prevention

When it happens

Trigger: A relay forwards a packet whose rlyMsg.SourceAddr.Ip fails net.ParseIP, is a private/loopback/reserved address while s.allowPrivateRelaySource is false, or has an invalid port (0).

Common situations: Relay deployed behind NAT reporting a private client IP; misconfigured relay stamping empty or malformed source IPs; operator not setting allowPrivateRelaySource when relays run on a private network.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/ed1baee2a32d13f0. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/server/msghandler.go:805

		return err
	}

	if rlyMsg.SourceAddr == nil {
		log.Error("server-relay[HandleRelayForward] missing source address")
		return fmt.Errorf("missing source address")
	}

	innerBytes, err := base64.StdEncoding.DecodeString(rlyMsg.InnerPacket)
	if err != nil {
		log.Error("server-relay[HandleRelayForward] failed to decode inner packet: %v", err)
		return err
	}

	realIP := net.ParseIP(rlyMsg.SourceAddr.Ip)
	if reason := validateRelaySourceAddr(realIP, rlyMsg.SourceAddr.Port, s.allowPrivateRelaySource.Load()); reason != "" {
		log.Warning("server-relay[HandleRelayForward] rejecting %s from relay %s: %s:%d",
			reason, ppd.ConnData.RemoteAddr.String(), rlyMsg.SourceAddr.Ip, rlyMsg.SourceAddr.Port)
		return fmt.Errorf("%s relay source address", reason)
	}
	realAddr := &net.UDPAddr{IP: realIP, Port: rlyMsg.SourceAddr.Port}

	relayAddrStr := ppd.ConnData.RemoteAddr.String()
	log.Info("server-relay[HandleRelayForward] from relay %s, real client %s, inner %d bytes",
		relayAddrStr, realAddr, len(innerBytes))

	// Allocate a pool packet for the inner bytes.
	innerPkt := s.device.AllocatePoolPacket()
	if len(innerBytes) > len(innerPkt.Buf) {
		s.device.ReleasePoolPacket(innerPkt)
		log.Warning("server-relay[HandleRelayForward] inner packet too large (%d bytes)", len(innerBytes))
		return fmt.Errorf("inner packet too large")
	}
	copy(innerPkt.Buf[:], innerBytes)
	innerPkt.Content = innerPkt.Buf[:len(innerBytes)]

	// Run standard RecvPrecheck on the inner packet.

View on GitHub (pinned to 6e04ca5ff0)