{"record":{"id":"31d7b304dec3f41f","repo":"OpenNHP/opennhp","slug":"received-flood-packet","errorCode":null,"errorMessage":"received flood packet","messagePattern":"received flood packet","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"nhp/core/responder.go","lineNumber":594,"sourceCode":"\t\t\t\tatomic.StoreInt32(&ppd.ConnData.RecvThreatCount, ThreatCountBeforeBlock)\n\t\t\t\t// block source address\n\t\t\t\tppd.ConnData.SendBlockSignal()\n\t\t\t}\n\t\t\terr = fmt.Errorf(\"received replay packet\")\n\t\t\treturn err\n\t\t}\n\t\tif remoteSendTime < ppd.ConnData.LastRemoteSendTime+MinimalRecvIntervalMs*int64(time.Millisecond) {\n\t\t\t// flood packet, drop\n\t\t\tlog.Critical(\"received flood packet from %s, drop packet\", ppd.ConnData.RemoteAddr.String())\n\t\t\t// threat plus 1\n\t\t\tthreat := atomic.AddInt32(&ppd.ConnData.RecvThreatCount, 1)\n\t\t\tif threat > ThreatCountBeforeBlock && !ppd.ConnData.IsClosed() {\n\t\t\t\t// clamp threat count to avoid overflow\n\t\t\t\tatomic.StoreInt32(&ppd.ConnData.RecvThreatCount, ThreatCountBeforeBlock)\n\t\t\t\t// block source address\n\t\t\t\tppd.ConnData.SendBlockSignal()\n\t\t\t}\n\t\t\terr = fmt.Errorf(\"received flood packet\")\n\t\t\treturn err\n\t\t}\n\t}\n\tif remoteSendTime < (ppd.LocalInitTime - 600*int64(time.Second)) {\n\t\t// send remote timestamp is too old than receive local time, drop\n\t\t// note there might be time calibration error between remote and local devices\n\t\tlog.Critical(\"received stale packet from %s, drop packet\", ppd.ConnData.RemoteAddr.String())\n\t\tthreat := atomic.AddInt32(&ppd.ConnData.RecvThreatCount, 1)\n\t\tif threat > ThreatCountBeforeBlock && !ppd.ConnData.IsClosed() {\n\t\t\t// clamp threat count to avoid overflow\n\t\t\tatomic.StoreInt32(&ppd.ConnData.RecvThreatCount, ThreatCountBeforeBlock)\n\t\t\t// block source address\n\t\t\tppd.ConnData.SendBlockSignal()\n\t\t}\n\t\terr = fmt.Errorf(\"received stale packet\")\n\t\treturn err\n\t}\n","sourceCodeStart":576,"sourceCodeEnd":612,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/responder.go#L576-L612","documentation":"validatePeer rejects packets that arrive sooner than MinimalRecvIntervalMs after the last accepted packet from the same peer (remoteSendTime < LastRemoteSendTime + minimal interval). This is a rate-limit/flood control: a peer (or attacker) sending packets faster than the allowed minimum interval is dropped and increments the threat count, eventually blocking the source address.","triggerScenarios":"A remote endpoint sends NHP packets with send timestamps less than MinimalRecvIntervalMs apart from the previously accepted one — e.g. a tight knock retry loop, a misconfigured agent with an interval below the server's MinimalRecvIntervalMs, or a flood attack.","commonSituations":"Agent retry/backoff configured shorter than the server's minimum recv interval, a bug causing duplicate sends per knock, stress-testing or load tooling hammering the responder, deliberate DoS attempts (which escalate to SendBlockSignal once the threat count passes ThreatCountBeforeBlock).","solutions":["Increase the client's knock/send interval so it is at or above the responder's MinimalRecvIntervalMs.","Add exponential backoff with jitter to client retry loops instead of fixed tight retries.","Check for duplicate-send bugs in wrappers or plugins that might fire the same knock multiple times per event.","If the source was blocked after repeated floods, unblock the address (firewall/block list on the AC or host) after fixing the client's send rate."],"exampleFix":"// before: agent config knocks faster than server minimum\n[knock]\nintervalMs = 100\n\n// after: respect server MinimalRecvIntervalMs\n[knock]\nintervalMs = 1000","handlingStrategy":"validation","validationCode":"// client-side: enforce the server's minimum interval before sending\nconst minimalRecvIntervalMs = int64(1000) // match server MinimalRecvIntervalMs\nif now-lastSendMs < minimalRecvIntervalMs {\n    time.Sleep(time.Duration(minimalRecvIntervalMs-(now-lastSendMs)) * time.Millisecond)\n}\nlastSendMs = time.Now().UnixMilli()","typeGuard":null,"tryCatchPattern":"if err := sendKnock(pkt); err != nil && strings.Contains(err.Error(), \"flood packet\") {\n    // back off exponentially before the next attempt\n    backoff := min(2*backoff, maxBackoff)\n    time.Sleep(backoff + jitter())\n}","preventionTips":["Configure client intervals at or above the server's MinimalRecvIntervalMs","Use exponential backoff with jitter in all retry loops","Audit plugins/wrappers for duplicate sends per event","Watch threat-count/block logs to detect runaway clients before they get blocked"],"tags":["rate-limiting","flood","udp","security"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}