Skip to content

Commit 0956e0d

Browse files
committed
Suppress spammy log messages in high-volume IP blocking or packet error situations
in these cases the log messages are doing more harm than good due to the performance impact of generating them.
1 parent 4145a31 commit 0956e0d

File tree

1 file changed

+41
-14
lines changed

1 file changed

+41
-14
lines changed

src/server/Server.php

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class Server implements ServerInterface{
4747

4848
private const RAKLIB_TPS = 100;
4949
private const RAKLIB_TIME_PER_TICK = 1 / self::RAKLIB_TPS;
50+
private const BLOCK_MESSAGE_SUPPRESSION_THRESHOLD = 2;
51+
private const PACKET_ERROR_SUPPRESSION_THRESHOLD = 2;
5052

5153
protected int $receiveBytes = 0;
5254
protected int $sendBytes = 0;
@@ -71,6 +73,10 @@ class Server implements ServerInterface{
7173
/** @var int[] string (address) => int (number of packets) */
7274
protected array $ipSec = [];
7375

76+
private int $blockedSinceLastUpdate = 0;
77+
78+
private int $packetErrorsSinceLastUpdate = 0;
79+
7480
/** @var string[] regex filters used to block out unwanted raw packets */
7581
protected array $rawPacketFilters = [];
7682

@@ -92,7 +98,9 @@ public function __construct(
9298
private ServerEventListener $eventListener,
9399
private ExceptionTraceCleaner $traceCleaner,
94100
private int $recvMaxSplitParts = ServerSession::DEFAULT_MAX_SPLIT_PART_COUNT,
95-
private int $recvMaxConcurrentSplits = ServerSession::DEFAULT_MAX_CONCURRENT_SPLIT_COUNT
101+
private int $recvMaxConcurrentSplits = ServerSession::DEFAULT_MAX_CONCURRENT_SPLIT_COUNT,
102+
private int $blockMessageSuppressionThreshold = self::BLOCK_MESSAGE_SUPPRESSION_THRESHOLD,
103+
private int $packetErrorSuppressionThreshold = self::PACKET_ERROR_SUPPRESSION_THRESHOLD
96104
){
97105
if($maxMtuSize < Session::MIN_MTU_SIZE){
98106
throw new \InvalidArgumentException("MTU size must be at least " . Session::MIN_MTU_SIZE . ", got $maxMtuSize");
@@ -183,6 +191,18 @@ private function tick() : void{
183191
$this->receiveBytes = 0;
184192
}
185193

194+
$packetErrorsWithoutMessage = $this->packetErrorsSinceLastUpdate - $this->packetErrorSuppressionThreshold;
195+
if($packetErrorsWithoutMessage > 0){
196+
$this->logger->warning("$packetErrorsWithoutMessage suppressed packet errors - RakLib may be under attack");
197+
}
198+
$this->packetErrorsSinceLastUpdate = 0;
199+
200+
$ipsBlockedWithoutMessage = $this->blockedSinceLastUpdate - $this->blockMessageSuppressionThreshold;
201+
if($ipsBlockedWithoutMessage > 0){
202+
$this->logger->warning("$ipsBlockedWithoutMessage more IP addresses were blocked - RakLib may be under attack");
203+
}
204+
$this->blockedSinceLastUpdate = 0;
205+
186206
if(count($this->block) > 0){
187207
asort($this->block);
188208
$now = time();
@@ -283,19 +303,22 @@ private function receivePacket() : bool{
283303
}
284304
}
285305
}catch(BinaryDataException $e){
286-
$logFn = function() use ($address, $e, $buffer) : void{
287-
$this->logger->debug("Packet from $address (" . strlen($buffer) . " bytes): 0x" . bin2hex($buffer));
288-
$this->logger->debug(get_class($e) . ": " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
289-
foreach($this->traceCleaner->getTrace(0, $e->getTrace()) as $line){
290-
$this->logger->debug($line);
306+
if($this->packetErrorsSinceLastUpdate < $this->packetErrorSuppressionThreshold){
307+
$logFn = function() use ($address, $e, $buffer) : void{
308+
$this->logger->debug("Packet from $address (" . strlen($buffer) . " bytes): 0x" . bin2hex($buffer));
309+
$this->logger->debug(get_class($e) . ": " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
310+
foreach($this->traceCleaner->getTrace(0, $e->getTrace()) as $line){
311+
$this->logger->debug($line);
312+
}
313+
$this->logger->error("Bad packet from $address: " . $e->getMessage());
314+
};
315+
if($this->logger instanceof \BufferedLogger){
316+
$this->logger->buffer($logFn);
317+
}else{
318+
$logFn();
291319
}
292-
$this->logger->error("Bad packet from $address: " . $e->getMessage());
293-
};
294-
if($this->logger instanceof \BufferedLogger){
295-
$this->logger->buffer($logFn);
296-
}else{
297-
$logFn();
298320
}
321+
$this->packetErrorsSinceLastUpdate++;
299322
$this->blockAddress($address->getIp(), 5);
300323
}
301324

@@ -354,10 +377,14 @@ public function blockAddress(string $address, int $timeout = 300) : void{
354377
if(!isset($this->block[$address]) or $timeout === -1){
355378
if($timeout === -1){
356379
$final = PHP_INT_MAX;
357-
}else{
358-
$this->logger->notice("Blocked $address for $timeout seconds");
380+
}
381+
if($this->blockedSinceLastUpdate < $this->blockMessageSuppressionThreshold){
382+
//Suppress additional log messages if multiple IPs have been banned in quick succession
383+
//In the case of IP spoofing attacks we don't want log spam to slow down the server
384+
$this->logger->notice("Blocked $address" . ($timeout === -1 ? " forever" : " for $timeout seconds"));
359385
}
360386
$this->block[$address] = $final;
387+
$this->blockedSinceLastUpdate++;
361388
}elseif($this->block[$address] < $final){
362389
$this->block[$address] = $final;
363390
}

0 commit comments

Comments
 (0)