diff options
Diffstat (limited to 'patches/server/0753-Add-packet-limiter-config.patch')
-rw-r--r-- | patches/server/0753-Add-packet-limiter-config.patch | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/patches/server/0753-Add-packet-limiter-config.patch b/patches/server/0753-Add-packet-limiter-config.patch new file mode 100644 index 0000000000..89443c567a --- /dev/null +++ b/patches/server/0753-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 bb66ecc34e2f618444ea0a3b106e6e4a2d5a0c70..d9a63e0574676357265c5b971b061b9595081d7d 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -519,4 +519,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) { |