Skip to content

Commit 5615ec2

Browse files
committed
Added experimental options to limit incoming packets
1 parent 9490dc3 commit 5615ec2

File tree

5 files changed

+131
-5
lines changed

5 files changed

+131
-5
lines changed

src/main/java/ua/nanit/limbo/configuration/LimboConfig.java

+26
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ public final class LimboConfig {
7171
private int bossGroupSize;
7272
private int workerGroupSize;
7373

74+
private boolean useTrafficLimits;
75+
private int maxPacketSize;
76+
private int maxPacketsPerSec;
77+
private int maxBytesPerSec;
78+
7479
public LimboConfig(Path root) {
7580
this.root = root;
7681
}
@@ -127,6 +132,11 @@ public void load() throws Exception {
127132
useEpoll = conf.node("netty", "useEpoll").getBoolean(true);
128133
bossGroupSize = conf.node("netty", "threads", "bossGroup").getInt(1);
129134
workerGroupSize = conf.node("netty", "threads", "workerGroup").getInt(4);
135+
136+
useTrafficLimits = conf.node("traffic", "enable").getBoolean(false);
137+
maxPacketSize = conf.node("traffic", "packetSize").getInt(-1);
138+
maxPacketsPerSec = conf.node("traffic", "packets").getInt(-1);
139+
maxBytesPerSec = conf.node("traffic", "bytes").getInt(-1);
130140
}
131141

132142
private BufferedReader getReader() throws IOException {
@@ -250,4 +260,20 @@ public int getBossGroupSize() {
250260
public int getWorkerGroupSize() {
251261
return workerGroupSize;
252262
}
263+
264+
public boolean isUseTrafficLimits() {
265+
return useTrafficLimits;
266+
}
267+
268+
public int getMaxPacketSize() {
269+
return maxPacketSize;
270+
}
271+
272+
public int getMaxPacketsPerSec() {
273+
return maxPacketsPerSec;
274+
}
275+
276+
public int getMaxBytesPerSec() {
277+
return maxBytesPerSec;
278+
}
253279
}

src/main/java/ua/nanit/limbo/connection/ClientChannelInitializer.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@
2121
import io.netty.channel.ChannelInitializer;
2222
import io.netty.channel.ChannelPipeline;
2323
import io.netty.handler.timeout.ReadTimeoutHandler;
24-
import ua.nanit.limbo.connection.pipeline.PacketDecoder;
25-
import ua.nanit.limbo.connection.pipeline.PacketEncoder;
26-
import ua.nanit.limbo.connection.pipeline.VarIntFrameDecoder;
27-
import ua.nanit.limbo.connection.pipeline.VarIntLengthEncoder;
24+
import ua.nanit.limbo.connection.pipeline.*;
2825
import ua.nanit.limbo.server.LimboServer;
2926

3027
import java.util.concurrent.TimeUnit;
@@ -49,6 +46,15 @@ protected void initChannel(Channel channel) {
4946
TimeUnit.MILLISECONDS));
5047
pipeline.addLast("frame_decoder", new VarIntFrameDecoder());
5148
pipeline.addLast("frame_encoder", new VarIntLengthEncoder());
49+
50+
if (server.getConfig().isUseTrafficLimits()) {
51+
pipeline.addLast("traffic_limit", new ChannelTrafficHandler(
52+
server.getConfig().getMaxPacketSize(),
53+
server.getConfig().getMaxPacketsPerSec(),
54+
server.getConfig().getMaxBytesPerSec()
55+
));
56+
}
57+
5258
pipeline.addLast("decoder", decoder);
5359
pipeline.addLast("encoder", encoder);
5460
pipeline.addLast("handler", connection);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package ua.nanit.limbo.connection.pipeline;
2+
3+
import io.netty.buffer.ByteBuf;
4+
import io.netty.channel.ChannelHandlerContext;
5+
import io.netty.channel.ChannelInboundHandlerAdapter;
6+
import org.jetbrains.annotations.NotNull;
7+
import ua.nanit.limbo.server.Logger;
8+
9+
public class ChannelTrafficHandler extends ChannelInboundHandlerAdapter {
10+
11+
private final int packetSize;
12+
private final int packetsPerSec;
13+
private final int bytesPerSec;
14+
15+
private int packetsCounter;
16+
private int bytesCounter;
17+
18+
private long lastPacket;
19+
20+
public ChannelTrafficHandler(int packetSize, int packetsPerSec, int bytesPerSec) {
21+
this.packetSize = packetSize;
22+
this.packetsPerSec = packetsPerSec;
23+
this.bytesPerSec = bytesPerSec;
24+
}
25+
26+
@Override
27+
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception {
28+
if (msg instanceof ByteBuf) {
29+
ByteBuf in = (ByteBuf) msg;
30+
int bytes = in.readableBytes();
31+
32+
System.out.println(bytes + " bytes");
33+
34+
if (packetSize > 0 && bytes > packetSize) {
35+
closeConnection(ctx, "Closed %s due too large packet size (%d bytes)", ctx.channel().remoteAddress(), bytes);
36+
return;
37+
}
38+
39+
if (!measureTraffic(ctx, bytes)) return;
40+
}
41+
42+
super.channelRead(ctx, msg);
43+
}
44+
45+
private boolean measureTraffic(ChannelHandlerContext ctx, int bytes) {
46+
if (packetsPerSec < 0 && bytesPerSec < 0) return true;
47+
48+
long time = System.currentTimeMillis();
49+
50+
if (time - lastPacket >= 1000) {
51+
bytesCounter = 0;
52+
packetsCounter = 0;
53+
}
54+
55+
packetsCounter++;
56+
bytesCounter += bytes;
57+
58+
if (packetsPerSec > 0 && packetsCounter > packetsPerSec) {
59+
closeConnection(ctx, "Closed %s due too frequent packet sending (%d per sec)", ctx.channel().remoteAddress(), packetsCounter);
60+
return false;
61+
}
62+
63+
if (bytesPerSec > 0 && bytesCounter > bytesPerSec) {
64+
closeConnection(ctx, "Closed %s due too many bytes sent per second (%d per sec)", ctx.channel().remoteAddress(), bytesCounter);
65+
return false;
66+
}
67+
68+
lastPacket = time;
69+
70+
return true;
71+
}
72+
73+
private void closeConnection(ChannelHandlerContext ctx, String reason, Object... args) {
74+
ctx.close();
75+
Logger.info(reason, args);
76+
}
77+
}

src/main/java/ua/nanit/limbo/connection/pipeline/VarIntFrameDecoder.java

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
4747
in.readerIndex(varIntEnd + 1);
4848
} else {
4949
int minimumRead = bytesRead + readVarInt;
50+
5051
if (in.isReadable(minimumRead)) {
5152
out.add(in.retainedSlice(varIntEnd + 1, readVarInt));
5253
in.skipBytes(minimumRead);

src/main/resources/settings.yml

+17-1
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,20 @@ netty:
112112
# EventLoopGroup threads count
113113
threads:
114114
bossGroup: 1
115-
workerGroup: 4
115+
workerGroup: 4
116+
117+
# [Experimental]
118+
# Options to check incoming traffic and kick potentially malicious connections.
119+
# Take into account that player can send many small packets, for example just moving mouse.
120+
traffic:
121+
# If true, then additional handler will be added to channel pipeline
122+
enable: false
123+
# Max packet size in bytes
124+
# Unlimited if -1
125+
packetSize: 1024
126+
# How many packets per second allowed for single connection
127+
# Ignored if -1
128+
packets: -1
129+
# How many bytes per second allowed for single connection
130+
# Ignored if -1
131+
bytes: 2048

0 commit comments

Comments
 (0)