mirror of
https://github.com/Nan1t/NanoLimbo.git
synced 2025-07-09 11:30:13 +02:00
Merge pull request #75 from Pantera07/dev/better-limit
Better Packet Limiter
This commit is contained in:
commit
f137495c4e
@ -73,8 +73,8 @@ public final class LimboConfig {
|
|||||||
|
|
||||||
private boolean useTrafficLimits;
|
private boolean useTrafficLimits;
|
||||||
private int maxPacketSize;
|
private int maxPacketSize;
|
||||||
private int maxPacketsPerSec;
|
private double interval;
|
||||||
private int maxBytesPerSec;
|
private double maxPacketRate;
|
||||||
|
|
||||||
public LimboConfig(Path root) {
|
public LimboConfig(Path root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
@ -134,9 +134,9 @@ public final class LimboConfig {
|
|||||||
workerGroupSize = conf.node("netty", "threads", "workerGroup").getInt(4);
|
workerGroupSize = conf.node("netty", "threads", "workerGroup").getInt(4);
|
||||||
|
|
||||||
useTrafficLimits = conf.node("traffic", "enable").getBoolean(false);
|
useTrafficLimits = conf.node("traffic", "enable").getBoolean(false);
|
||||||
maxPacketSize = conf.node("traffic", "packetSize").getInt(-1);
|
maxPacketSize = conf.node("traffic", "maxPacketSize").getInt(-1);
|
||||||
maxPacketsPerSec = conf.node("traffic", "packets").getInt(-1);
|
interval = conf.node("traffic", "interval").getDouble(-1.0);
|
||||||
maxBytesPerSec = conf.node("traffic", "bytes").getInt(-1);
|
maxPacketRate = conf.node("traffic", "maxPacketRate").getDouble(-1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedReader getReader() throws IOException {
|
private BufferedReader getReader() throws IOException {
|
||||||
@ -269,11 +269,11 @@ public final class LimboConfig {
|
|||||||
return maxPacketSize;
|
return maxPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxPacketsPerSec() {
|
public double getInterval() {
|
||||||
return maxPacketsPerSec;
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxBytesPerSec() {
|
public double getMaxPacketRate() {
|
||||||
return maxBytesPerSec;
|
return maxPacketRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,8 +50,8 @@ public class ClientChannelInitializer extends ChannelInitializer<Channel> {
|
|||||||
if (server.getConfig().isUseTrafficLimits()) {
|
if (server.getConfig().isUseTrafficLimits()) {
|
||||||
pipeline.addLast("traffic_limit", new ChannelTrafficHandler(
|
pipeline.addLast("traffic_limit", new ChannelTrafficHandler(
|
||||||
server.getConfig().getMaxPacketSize(),
|
server.getConfig().getMaxPacketSize(),
|
||||||
server.getConfig().getMaxPacketsPerSec(),
|
server.getConfig().getInterval(),
|
||||||
server.getConfig().getMaxBytesPerSec()
|
server.getConfig().getMaxPacketRate()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,21 +6,18 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import ua.nanit.limbo.server.Logger;
|
import ua.nanit.limbo.server.Logger;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class ChannelTrafficHandler extends ChannelInboundHandlerAdapter {
|
public class ChannelTrafficHandler extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
private final int packetSize;
|
private final int maxPacketSize;
|
||||||
private final int packetsPerSec;
|
private final double maxPacketRate;
|
||||||
private final int bytesPerSec;
|
private final PacketBucket packetBucket;
|
||||||
|
|
||||||
private int packetsCounter;
|
public ChannelTrafficHandler(int maxPacketSize, double interval, double maxPacketRate) {
|
||||||
private int bytesCounter;
|
this.maxPacketSize = maxPacketSize;
|
||||||
|
this.maxPacketRate = maxPacketRate;
|
||||||
private long lastPacket;
|
this.packetBucket = (interval > 0.0 && maxPacketRate > 0.0) ? new PacketBucket(interval * 1000.0, 150) : null;
|
||||||
|
|
||||||
public ChannelTrafficHandler(int packetSize, int packetsPerSec, int bytesPerSec) {
|
|
||||||
this.packetSize = packetSize;
|
|
||||||
this.packetsPerSec = packetsPerSec;
|
|
||||||
this.bytesPerSec = bytesPerSec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -29,47 +26,86 @@ public class ChannelTrafficHandler extends ChannelInboundHandlerAdapter {
|
|||||||
ByteBuf in = (ByteBuf) msg;
|
ByteBuf in = (ByteBuf) msg;
|
||||||
int bytes = in.readableBytes();
|
int bytes = in.readableBytes();
|
||||||
|
|
||||||
if (packetSize > 0 && bytes > packetSize) {
|
if (maxPacketSize > 0 && bytes > maxPacketSize) {
|
||||||
closeConnection(ctx, "Closed %s due too large packet size (%d bytes)", ctx.channel().remoteAddress(), bytes);
|
closeConnection(ctx, "Closed %s due to large packet size (%d bytes)", ctx.channel().remoteAddress(), bytes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!measureTraffic(ctx, bytes)) return;
|
if (packetBucket != null) {
|
||||||
|
packetBucket.incrementPackets(1);
|
||||||
|
if (packetBucket.getCurrentPacketRate() > maxPacketRate) {
|
||||||
|
closeConnection(ctx, "Closed %s due to many packets sent (%d in the last %.1f seconds)", ctx.channel().remoteAddress(), packetBucket.sum, (packetBucket.intervalTime / 1000.0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.channelRead(ctx, msg);
|
super.channelRead(ctx, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean measureTraffic(ChannelHandlerContext ctx, int bytes) {
|
|
||||||
if (packetsPerSec < 0 && bytesPerSec < 0) return true;
|
|
||||||
|
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
|
|
||||||
if (time - lastPacket >= 1000) {
|
|
||||||
bytesCounter = 0;
|
|
||||||
packetsCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
packetsCounter++;
|
|
||||||
bytesCounter += bytes;
|
|
||||||
|
|
||||||
if (packetsPerSec > 0 && packetsCounter > packetsPerSec) {
|
|
||||||
closeConnection(ctx, "Closed %s due too frequent packet sending (%d per sec)", ctx.channel().remoteAddress(), packetsCounter);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesPerSec > 0 && bytesCounter > bytesPerSec) {
|
|
||||||
closeConnection(ctx, "Closed %s due too many bytes sent per second (%d per sec)", ctx.channel().remoteAddress(), bytesCounter);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastPacket = time;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeConnection(ChannelHandlerContext ctx, String reason, Object... args) {
|
private void closeConnection(ChannelHandlerContext ctx, String reason, Object... args) {
|
||||||
ctx.close();
|
ctx.close();
|
||||||
Logger.info(reason, args);
|
Logger.info(reason, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class PacketBucket {
|
||||||
|
private static final double NANOSECONDS_TO_MILLISECONDS = 1.0e-6;
|
||||||
|
private static final int MILLISECONDS_TO_SECONDS = 1000;
|
||||||
|
|
||||||
|
private final double intervalTime;
|
||||||
|
private final double intervalResolution;
|
||||||
|
private final int[] data;
|
||||||
|
private int newestData;
|
||||||
|
private double lastBucketTime;
|
||||||
|
private int sum;
|
||||||
|
|
||||||
|
public PacketBucket(final double intervalTime, final int totalBuckets) {
|
||||||
|
this.intervalTime = intervalTime;
|
||||||
|
this.intervalResolution = intervalTime / totalBuckets;
|
||||||
|
this.data = new int[totalBuckets];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementPackets(final int packets) {
|
||||||
|
double timeMs = System.nanoTime() * NANOSECONDS_TO_MILLISECONDS;
|
||||||
|
double timeDelta = timeMs - this.lastBucketTime;
|
||||||
|
|
||||||
|
if (timeDelta < 0.0) {
|
||||||
|
timeDelta = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeDelta < this.intervalResolution) {
|
||||||
|
this.data[this.newestData] += packets;
|
||||||
|
this.sum += packets;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bucketsToMove = (int)(timeDelta / this.intervalResolution);
|
||||||
|
double nextBucketTime = this.lastBucketTime + bucketsToMove * this.intervalResolution;
|
||||||
|
|
||||||
|
if (bucketsToMove >= this.data.length) {
|
||||||
|
Arrays.fill(this.data, 0);
|
||||||
|
this.data[0] = packets;
|
||||||
|
this.sum = packets;
|
||||||
|
this.newestData = 0;
|
||||||
|
this.lastBucketTime = timeMs;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < bucketsToMove; ++i) {
|
||||||
|
int index = (this.newestData + i) % this.data.length;
|
||||||
|
this.sum -= this.data[index];
|
||||||
|
this.data[index] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newestDataIndex = (this.newestData + bucketsToMove) % this.data.length;
|
||||||
|
this.sum += packets - this.data[newestDataIndex];
|
||||||
|
this.data[newestDataIndex] = packets;
|
||||||
|
this.newestData = newestDataIndex;
|
||||||
|
this.lastBucketTime = nextBucketTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCurrentPacketRate() {
|
||||||
|
return this.sum / (this.intervalTime / MILLISECONDS_TO_SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,18 +114,20 @@ netty:
|
|||||||
bossGroup: 1
|
bossGroup: 1
|
||||||
workerGroup: 4
|
workerGroup: 4
|
||||||
|
|
||||||
# [Experimental]
|
|
||||||
# Options to check incoming traffic and kick potentially malicious connections.
|
# Options to check incoming traffic and kick potentially malicious connections.
|
||||||
# Take into account that player can send many small packets, for example just moving mouse.
|
# Take into account that player can send many small packets, for example just moving mouse.
|
||||||
traffic:
|
traffic:
|
||||||
# If true, then additional handler will be added to channel pipeline
|
# If true, then additional handler will be added to channel pipeline
|
||||||
enable: false
|
enable: true
|
||||||
# Max packet size in bytes
|
# Max packet size in bytes
|
||||||
# Unlimited if -1
|
# Unlimited if -1
|
||||||
packetSize: 1024
|
maxPacketSize: 8192
|
||||||
# How many packets per second allowed for single connection
|
# The interval to measure packets over
|
||||||
# Ignored if -1
|
# Lowering this value will limit peak packets from players which would target people with bad connections
|
||||||
packets: -1
|
# Raising this value will allow higher peak packet rates, which will help with people who have poor connections
|
||||||
# How many bytes per second allowed for single connection
|
# Ignored if -1.0
|
||||||
# Ignored if -1
|
interval: 7.0
|
||||||
bytes: 2048
|
# The maximum maximum packets per second for players
|
||||||
|
# It is measured over the configured interval
|
||||||
|
# Ignored if -1.0
|
||||||
|
maxPacketRate: 500.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user