Middleware (#662)

* removed suspicious shadowing and clarified failure message; customized skipped bundling

* smp history is now a no-less-efficient circular array

* adjustment to bundle dispatch timing; adjustment to inbound sequence reorder queue

* adjustments to handling inbound packets with missing subslots

* unused PacketCoding features

* comments; delayed start of the queue processor task; turned sequence reorder task and subslot missing task into function literals

* optimizations to the inbound re-order by sequence routines by controlling execution flow

* the subslot request timer has been separated from the standard bundling processor; config values for bundling, sequence resolution, and subslot requests in the middleware actor have been included

* replacing func-array with conditional logic
This commit is contained in:
Fate-JH 2021-01-12 14:33:44 -05:00 committed by GitHub
parent 563afcdb19
commit b5fc2ecf70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 649 additions and 248 deletions

View file

@ -99,6 +99,23 @@ anti-cheat {
}
network {
middleware {
# How often between executions of the outbound bundling process
packet-bundling-delay = 25 milliseconds
# Pause inbound packet transmission towards the network if the sequence number is out of order
# Packets are put aside until the sequence is restored, or this timeout passes
in-reorder-timeout = 50 milliseconds
# Wait on inbound packets if that packet is a SlottedMetaPacket and the next subslot number is greater than expected
# Does not stop the transmission of packets to the server
# but dictates how long between requests to the network (client) for missing packets with anticipated subslot numbers
in-subslot-missing-delay = 50 milliseconds
# How many attempts at resolving missing packets with anticipated subslot numbers
in-subslot-missing-attempts = 10
}
session {
# The maximum amount of time since the last inbound packet from a UDP session
# before it is dropped.

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017 PSForever
package net.psforever.packet
import java.security.{Key, SecureRandom, Security}
import java.security.{Key, Security}
import javax.crypto.Cipher
import javax.crypto.spec.RC5ParameterSpec
@ -16,8 +16,6 @@ import net.psforever.util.Md5Mac
object PacketCoding {
Security.addProvider(new BouncyCastleProvider)
private val random = new SecureRandom()
val RC5_BLOCK_SIZE = 8
/** A lower bound on the packet size */
@ -40,9 +38,9 @@ object PacketCoding {
case _: PlanetSideControlPacket if crypto.isEmpty => BitVector.empty
case _ =>
sequence match {
case Some(sequence) =>
uint16L.encode(sequence) match {
case Successful(seq) => seq
case Some(_sequence) =>
uint16L.encode(_sequence) match {
case Successful(_seq) => _seq
case f @ Failure(_) => return f
}
case None =>
@ -53,8 +51,8 @@ object PacketCoding {
val (flags, payload) = packet match {
case _: PlanetSideGamePacket | _: PlanetSideControlPacket if crypto.isDefined =>
encodePacket(packet) match {
case Successful(payload) =>
val encryptedPayload = crypto.get.encrypt(payload.bytes) match {
case Successful(_payload) =>
val encryptedPayload = crypto.get.encrypt(_payload.bytes) match {
case Successful(p) => p
case f: Failure => return f
}
@ -68,29 +66,29 @@ object PacketCoding {
}
case packet: PlanetSideGamePacket =>
encodePacket(packet) match {
case Successful(payload) =>
case Successful(_payload) =>
(
PlanetSidePacketFlags.codec.encode(PlanetSidePacketFlags(PacketType.Normal, secured = false)).require,
payload
_payload
)
case f @ Failure(_) => return f
}
case packet: PlanetSideControlPacket =>
encodePacket(packet) match {
case Successful(payload) =>
case Successful(_payload) =>
(
// control packets don't have flags
BitVector.empty,
payload
_payload
)
case f @ Failure(_) => return f
}
case packet: PlanetSideCryptoPacket =>
encodePacket(packet) match {
case Successful(payload) =>
case Successful(_payload) =>
(
PlanetSidePacketFlags.codec.encode(PlanetSidePacketFlags(PacketType.Crypto, secured = false)).require,
payload
_payload
)
case f @ Failure(_) => return f
}
@ -164,7 +162,7 @@ object PacketCoding {
crypto: Option[CryptoCoding] = None
): Attempt[(PlanetSidePacket, Int)] = {
val (flags, remainder) = Codec.decode[PlanetSidePacketFlags](BitVector(msg)) match {
case Successful(DecodeResult(value, remainder)) => (value, remainder)
case Successful(DecodeResult(value, _remainder)) => (value, _remainder)
case Failure(e) => return Failure(Err(s"Failed to parse packet flags: ${e.message}"))
}
@ -184,8 +182,8 @@ object PacketCoding {
// all packets have a two byte sequence ID
val (sequence, payload) = uint16L.decode(remainder) match {
case Successful(DecodeResult(value, remainder)) =>
(value, remainder.toByteVector)
case Successful(DecodeResult(value, _remainder)) =>
(value, _remainder.toByteVector)
case Failure(e) =>
return Failure(Err(s"Failed to parse packet sequence number: ${e.message}"))
}
@ -195,9 +193,9 @@ object PacketCoding {
CryptoPacketOpcode
.getPacketDecoder(cryptoState)(payload.bits)
.map(p => (p.value.asInstanceOf[PlanetSidePacket], sequence))
case (PacketType.Normal, Some(crypto)) if flags.secured =>
case (PacketType.Normal, Some(_crypto)) if flags.secured =>
// encrypted payload is 4-byte aligned: 1b flags, 2b sequence, 1b padding
crypto.decrypt(payload.drop(1)).map(p => decodePacket(p)).flatten.map(p => (p, sequence))
_crypto.decrypt(payload.drop(1)).map(p => decodePacket(p)).flatten.map(p => (p, sequence))
case (PacketType.Normal, None) if !flags.secured =>
decodePacket(payload).map(p => (p, sequence))
case (PacketType.Normal, None) =>
@ -279,7 +277,9 @@ object PacketCoding {
Successful(ByteVector.view(rc5Encrypt.doFinal(packetWithPadding.toArray)))
}
} catch {
case e: Throwable => Failure(Err(s"encrypt error: '${e.getMessage}' data: ${packetWithPadding.toHex}"))
case e: Throwable =>
val msg = if(e.getMessage == null) e.getClass.getSimpleName else e.getMessage
Failure(Err(s"encrypt error: '$msg' data: ${packetWithPadding.toHex}"))
}
}
@ -295,7 +295,7 @@ object PacketCoding {
// last byte is the padding length
val padding = uint8L.decode(payloadDecrypted.takeRight(1).bits) match {
case Successful(padding) => padding.value
case Successful(_padding) => _padding.value
case Failure(e) => return Failure(Err(s"Failed to decode the encrypted padding length: ${e.message}"))
}
@ -304,7 +304,7 @@ object PacketCoding {
val mac = bytes(Md5Mac.MACLENGTH).decode(payloadMac.bits) match {
case Failure(e) => return Failure(Err("Failed to extract the encrypted MAC: " + e.message))
case Successful(mac) => mac.value
case Successful(_mac) => _mac.value
}
val payloadNoMac = payloadNoPadding.dropRight(Md5Mac.MACLENGTH)

View file

@ -114,7 +114,15 @@ case class AntiCheatConfig(
)
case class NetworkConfig(
session: SessionConfig
session: SessionConfig,
middleware: MiddlewareConfig
)
case class MiddlewareConfig(
packetBundlingDelay: FiniteDuration,
inReorderTimeout: FiniteDuration,
inSubslotMissingDelay: FiniteDuration,
inSubslotMissingAttempts: Int
)
case class SessionConfig(