aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0750-Add-packet-limiter-config.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0750-Add-packet-limiter-config.patch')
-rw-r--r--patches/server/0750-Add-packet-limiter-config.patch205
1 files changed, 205 insertions, 0 deletions
diff --git a/patches/server/0750-Add-packet-limiter-config.patch b/patches/server/0750-Add-packet-limiter-config.patch
new file mode 100644
index 0000000000..feb1bfd26f
--- /dev/null
+++ b/patches/server/0750-Add-packet-limiter-config.patch
@@ -0,0 +1,205 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Fri, 30 Oct 2020 22:37:16 -0700
+Subject: [PATCH] Add packet limiter config
+
+Example config:
+packet-limiter:
+ kick-message: '&cSent too many packets'
+ limits:
+ all:
+ interval: 7.0
+ max-packet-rate: 500.0
+ PacketPlayInAutoRecipe:
+ interval: 4.0
+ max-packet-rate: 5.0
+ action: DROP
+
+all section refers to all incoming packets, the action for all is
+hard coded to KICK.
+
+For specific limits, the section name is the class's name,
+and an action can be defined: DROP or KICK
+
+If interval or rate are less-than 0, the limit is ignored
+
+diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
+index bc91acb8b7e407092102e39037fc6f3fd380a073..da3eca94623a81d178166cc4689bb95a829837cc 100644
+--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
+@@ -521,4 +521,102 @@ public class PaperConfig {
+ itemValidationBookAuthorLength = getInt("settings.item-validation.book.author", itemValidationBookAuthorLength);
+ itemValidationBookPageLength = getInt("settings.item-validation.book.page", itemValidationBookPageLength);
+ }
++
++ public static final class PacketLimit {
++ public final double packetLimitInterval;
++ public final double maxPacketRate;
++ public final ViolateAction violateAction;
++
++ public PacketLimit(final double packetLimitInterval, final double maxPacketRate, final ViolateAction violateAction) {
++ this.packetLimitInterval = packetLimitInterval;
++ this.maxPacketRate = maxPacketRate;
++ this.violateAction = violateAction;
++ }
++
++ public static enum ViolateAction {
++ KICK, DROP;
++ }
++ }
++
++ public static String kickMessage;
++ public static PacketLimit allPacketsLimit;
++ public static java.util.Map<Class<? extends net.minecraft.network.protocol.Packet<?>>, PacketLimit> packetSpecificLimits = new java.util.HashMap<>();
++
++ private static void packetLimiter() {
++ packetSpecificLimits.clear();
++ kickMessage = org.bukkit.ChatColor.translateAlternateColorCodes('&', getString("settings.packet-limiter.kick-message", "&cSent too many packets"));
++ allPacketsLimit = new PacketLimit(
++ getDouble("settings.packet-limiter.limits.all.interval", 7.0),
++ getDouble("settings.packet-limiter.limits.all.max-packet-rate", 500.0),
++ PacketLimit.ViolateAction.KICK
++ );
++ if (allPacketsLimit.maxPacketRate <= 0.0 || allPacketsLimit.packetLimitInterval <= 0.0) {
++ allPacketsLimit = null;
++ }
++ final ConfigurationSection section = config.getConfigurationSection("settings.packet-limiter.limits");
++
++ // add default packets
++
++ // auto recipe limiting
++ getDouble("settings.packet-limiter.limits." +
++ "PacketPlayInAutoRecipe" + ".interval", 4.0);
++ getDouble("settings.packet-limiter.limits." +
++ "PacketPlayInAutoRecipe" + ".max-packet-rate", 5.0);
++ getString("settings.packet-limiter.limits." +
++ "PacketPlayInAutoRecipe" + ".action", PacketLimit.ViolateAction.DROP.name());
++
++ final Map<String, String> mojangToSpigot = new HashMap<>();
++ final Map<String, io.papermc.paper.util.ObfHelper.ClassMapping> maps = io.papermc.paper.util.ObfHelper.INSTANCE.mappingsByObfName();
++ if (maps != null) {
++ maps.forEach((spigotName, classMapping) ->
++ mojangToSpigot.put(classMapping.mojangName(), classMapping.obfName()));
++ }
++
++ for (final String packetClassName : section.getKeys(false)) {
++ if (packetClassName.equals("all")) {
++ continue;
++ }
++ Class<?> packetClazz = null;
++
++ for (final String subpackage : List.of("game", "handshake", "login", "status")) {
++ final String fullName = "net.minecraft.network.protocol." + subpackage + "." + packetClassName;
++ try {
++ packetClazz = Class.forName(fullName);
++ break;
++ } catch (final ClassNotFoundException ex) {
++ try {
++ final String spigot = mojangToSpigot.get(fullName);
++ if (spigot != null) {
++ packetClazz = Class.forName(spigot);
++ }
++ } catch (final ClassNotFoundException ignore) {}
++ }
++ }
++
++ if (packetClazz == null || !net.minecraft.network.protocol.Packet.class.isAssignableFrom(packetClazz)) {
++ MinecraftServer.LOGGER.warn("Packet '" + packetClassName + "' does not exist, cannot limit it! Please update paper.yml");
++ continue;
++ }
++
++ if (!(section.get(packetClassName.concat(".interval")) instanceof Number) || !(section.get(packetClassName.concat(".max-packet-rate")) instanceof Number)) {
++ throw new RuntimeException("Packet limit setting " + packetClassName + " is missing interval or max-packet-rate!");
++ }
++
++ final String actionString = section.getString(packetClassName.concat(".action"), "KICK");
++ PacketLimit.ViolateAction action = PacketLimit.ViolateAction.KICK;
++ for (PacketLimit.ViolateAction test : PacketLimit.ViolateAction.values()) {
++ if (actionString.equalsIgnoreCase(test.name())) {
++ action = test;
++ break;
++ }
++ }
++
++ final double interval = section.getDouble(packetClassName.concat(".interval"));
++ final double rate = section.getDouble(packetClassName.concat(".max-packet-rate"));
++
++ if (interval > 0.0 && rate > 0.0) {
++ packetSpecificLimits.put((Class)packetClazz, new PacketLimit(interval, rate, action));
++ }
++ }
++ }
+ }
+diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
+index 47eadb4a08953a45300d769518af22b1463f4d11..b27610cde8eaa7ff35c777039a0ca9d8eab748fe 100644
+--- a/src/main/java/net/minecraft/network/Connection.java
++++ b/src/main/java/net/minecraft/network/Connection.java
+@@ -136,6 +136,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+ }
+ }
+ // Paper end - allow controlled flushing
++ // Paper start - packet limiter
++ protected final Object PACKET_LIMIT_LOCK = new Object();
++ protected final io.papermc.paper.util.IntervalledCounter allPacketCounts = com.destroystokyo.paper.PaperConfig.allPacketsLimit != null ? new io.papermc.paper.util.IntervalledCounter(
++ (long)(com.destroystokyo.paper.PaperConfig.allPacketsLimit.packetLimitInterval * 1.0e9)
++ ) : null;
++ protected final java.util.Map<Class<? extends net.minecraft.network.protocol.Packet<?>>, io.papermc.paper.util.IntervalledCounter> packetSpecificLimits = new java.util.HashMap<>();
++
++ private boolean stopReadingPackets;
++ private void killForPacketSpam() {
++ this.sendPacket(new ClientboundDisconnectPacket(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(com.destroystokyo.paper.PaperConfig.kickMessage, true)[0]), (future) -> {
++ this.disconnect(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(com.destroystokyo.paper.PaperConfig.kickMessage, true)[0]);
++ });
++ this.setReadOnly();
++ this.stopReadingPackets = true;
++ }
++ // Paper end - packet limiter
+
+ public Connection(PacketFlow side) {
+ this.receiving = side;
+@@ -216,6 +232,45 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+
+ protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
+ if (this.channel.isOpen()) {
++ // Paper start - packet limiter
++ if (this.stopReadingPackets) {
++ return;
++ }
++ if (this.allPacketCounts != null ||
++ com.destroystokyo.paper.PaperConfig.packetSpecificLimits.containsKey(packet.getClass())) {
++ long time = System.nanoTime();
++ synchronized (PACKET_LIMIT_LOCK) {
++ if (this.allPacketCounts != null) {
++ this.allPacketCounts.updateAndAdd(1, time);
++ if (this.allPacketCounts.getRate() >= com.destroystokyo.paper.PaperConfig.allPacketsLimit.maxPacketRate) {
++ this.killForPacketSpam();
++ return;
++ }
++ }
++
++ for (Class<?> check = packet.getClass(); check != Object.class; check = check.getSuperclass()) {
++ com.destroystokyo.paper.PaperConfig.PacketLimit packetSpecificLimit =
++ com.destroystokyo.paper.PaperConfig.packetSpecificLimits.get(check);
++ if (packetSpecificLimit == null) {
++ continue;
++ }
++ io.papermc.paper.util.IntervalledCounter counter = this.packetSpecificLimits.computeIfAbsent((Class)check, (clazz) -> {
++ return new io.papermc.paper.util.IntervalledCounter((long)(packetSpecificLimit.packetLimitInterval * 1.0e9));
++ });
++ counter.updateAndAdd(1, time);
++ if (counter.getRate() >= packetSpecificLimit.maxPacketRate) {
++ switch (packetSpecificLimit.violateAction) {
++ case DROP:
++ return;
++ case KICK:
++ this.killForPacketSpam();
++ return;
++ }
++ }
++ }
++ }
++ }
++ // Paper end - packet limiter
+ try {
+ Connection.genericsFtw(packet, this.packetListener);
+ } catch (RunningOnDifferentThreadException cancelledpackethandleexception) {