DetectionSeverity
DetectionSeverity
#Overview
name: DetectionSeverity
The value of this variable can be defined or overridden in .ini config files. 19
.ini config files referencing this setting variable.
It is referenced in 21
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of DetectionSeverity is to manage different levels of severity for RPC DoS (Denial of Service) detection in Unreal Engine’s networking system. This variable is used to define and control the escalating states of DoS detection based on the amount of RPC (Remote Procedure Call) spam detected.
DetectionSeverity is primarily used in the networking subsystem of Unreal Engine, specifically in the RPC DoS detection module. It’s referenced in the Engine’s networking code, particularly in the RPCDoSDetection and DDoSDetection classes.
The value of this variable is typically set during the initialization of the DoS detection system. It’s populated from configuration files, specifically from the GEngineIni file, where different severity categories are defined.
DetectionSeverity interacts with several other variables and systems:
- ActiveState: Represents the current severity state.
- WorstActiveState: Tracks the worst severity state reached.
- CounterPerSecHistory: Stores historical data for quota calculations.
Developers should be aware of several things when using this variable:
- The severity states are defined in configuration files and loaded at runtime.
- The system automatically escalates or de-escalates between these states based on detected RPC activity.
- Each state can have different quota limits, cooldown times, and actions (like kicking players).
Best practices for using this variable include:
- Carefully configure the severity states in the engine configuration files.
- Monitor logs for state changes to track potential DoS attacks.
- Balance the states to provide adequate protection without unnecessarily impacting legitimate players.
- Regularly review and adjust the configuration based on observed network behavior and any detected attacks.
Remember that this system is crucial for protecting the game servers from potential DoS attacks, so it should be configured and monitored carefully.
#Setting Variables
#References In INI files
<Workspace>/Engine/Config/BaseEngine.ini:1652, section: [DDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1653, section: [DDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1654, section: [DDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1655, section: [DDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1656, section: [DDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1706, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1707, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1708, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1709, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1710, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1711, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1712, section: [GameNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1720, section: [BeaconNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1721, section: [BeaconNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1722, section: [BeaconNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1723, section: [BeaconNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1724, section: [BeaconNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1725, section: [BeaconNetDriver RPCDoSDetection]
<Workspace>/Engine/Config/BaseEngine.ini:1726, section: [BeaconNetDriver RPCDoSDetection]
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:424
Scope (from outer to inner):
file
function void FRPCDoSDetection::InitConfig
Source code excerpt:
DetectionSeverity.Empty();
if (bRPCDoSDetection)
{
int32 HighestHistoryRequirment = 0;
if (CurConfigObj != nullptr)
{
TArray<FString>& SeverityCategories = CurConfigObj->DetectionSeverity;
const TCHAR* RPCDoSSectionName = URPCDoSDetectionConfig::GetConfigSectionName();
for (const FString& CurCategory : SeverityCategories)
{
FString CurSection = FString(RPCDoSSectionName) + TEXT(".") + CurCategory;
if (GConfig->DoesSectionExist(*CurSection, GEngineIni))
{
FRPCDoSStateConfig& CurState = DetectionSeverity.AddDefaulted_GetRef();
CurState.SeverityCategory = CurCategory;
bool bSuccess = CurState.LoadStructConfig(*CurSection, *GEngineIni);
if (bSuccess)
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:466
Scope (from outer to inner):
file
function void FRPCDoSDetection::InitConfig
Source code excerpt:
}
if (DetectionSeverity.Num() > 0)
{
DetectionSeverity[ActiveState].ApplyState(*this);
CounterPerSecHistory.SetNum(HighestHistoryRequirment);
}
else
{
UE_LOG(LogNet, Warning, TEXT("RPC DoS detection enabled, but no DetectionSeverity states specified! Disabling."));
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:506
Scope (from outer to inner):
file
function void FRPCDoSDetection::UpdateSeverity_Private
Source code excerpt:
{
bool bEscalate = Update == ERPCDoSSeverityUpdate::Escalate || Update == ERPCDoSSeverityUpdate::AutoEscalate;
int32 NewStateIdx = FMath::Clamp(ActiveState + (bEscalate ? 1 : -1), 0, DetectionSeverity.Num()-1);
// If kicking is disabled, and the new state is the kick state, exclude that state (otherwise RPC DoS Detection will become stuck)
const bool bPreventKick = CVarAllowRPCDoSDetectionKicking.GetValueOnAnyThread() == 0;
if (DetectionSeverity[NewStateIdx].bKickPlayer && bPreventKick)
{
NewStateIdx = ActiveState;
}
if (NewStateIdx != ActiveState)
{
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:538
Scope (from outer to inner):
file
function void FRPCDoSDetection::UpdateSeverity_Private
Source code excerpt:
while (bCooloffReached && NewStateIdx > 0)
{
FRPCDoSStateConfig& PrevState = DetectionSeverity[NewStateIdx-1];
FRPCDoSStateConfig& CurState = DetectionSeverity[NewStateIdx];
int32 CurStateCooloffTime = CurState.CooloffTime;
const TArray<int8>& CurStateTimePeriods = CurState.GetAllTimePeriods();
FRPCDoSCounters CurPerPeriodHistory[16] = {};
check(CounterPerSecHistory.Num() >= CurStateCooloffTime);
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:577
Scope (from outer to inner):
file
function void FRPCDoSDetection::UpdateSeverity_Private
Source code excerpt:
FRPCDoSStateConfig& OldState = DetectionSeverity[ActiveState];
while (true)
{
FRPCDoSStateConfig& CurState = DetectionSeverity[NewStateIdx];
ActiveState = NewStateIdx;
CurState.ApplyState(*this);
const TArray<int8>& NeededTimePeriods = DetectionSeverity[ActiveState].GetAllTimePeriods();
RecalculatePeriodHistory(NeededTimePeriods, CounterPerPeriodHistory);
// If escalating, keep escalating until the quota checks fail
if (bEscalate && (HasHitQuota_Count(CounterPerPeriodHistory, TickScope.FrameCounter) ||
HasHitQuota_Time(CounterPerPeriodHistory, TickScope.FrameCounter)))
{
NewStateIdx = FMath::Clamp(ActiveState + 1, 0, DetectionSeverity.Num()-1);
if (DetectionSeverity[NewStateIdx].bKickPlayer && bPreventKick)
{
NewStateIdx = ActiveState;
break;
}
if (NewStateIdx == ActiveState)
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:616
Scope (from outer to inner):
file
function void FRPCDoSDetection::UpdateSeverity_Private
Source code excerpt:
FRPCDoSStateConfig& NewState = DetectionSeverity[NewStateIdx];
if (bLogEscalate)
{
UE_LOG(LogNet, Warning, TEXT("Updated RPC DoS detection severity for '%s' from '%s' to '%s' (Reason: %s)"),
*GetPlayerAddress(), *OldState.SeverityCategory, *NewState.SeverityCategory, LexToString(Reason));
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:714
Scope (from outer to inner):
file
function void FRPCDoSDetection::UpdateSeverity_Private
Source code excerpt:
{
UE_LOG(LogNet, Warning, TEXT("Updated analytics RPC DoS detection severity from '%s' to '%s'."),
*DetectionSeverity[OldWorstState].SeverityCategory, *NewState.SeverityCategory);
// Log RPC's which could not be timed individually (as they won't display in analytics)
if (UntimedRPCs.Num() > 0)
{
TStringBuilder<2048> RPCGroupStr;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetection.cpp:830
Scope (from outer to inner):
file
function void FRPCDoSDetection::PreTickDispatch
Source code excerpt:
SecondCounter.ResetRPCCounters();
const TArray<int8>& NeededTimePeriods = DetectionSeverity[ActiveState].GetAllTimePeriods();
RecalculatePeriodHistory(NeededTimePeriods, CounterPerPeriodHistory);
SecondsIncrementer++;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Net/RPCDoSDetectionConfig.h:74
Scope (from outer to inner):
file
class class URPCDoSDetectionConfig : public UObject
Source code excerpt:
/** Names of the different RPC DoS detection states, for escalating severity, depending on the amount of RPC spam */
UPROPERTY(config)
TArray<FString> DetectionSeverity;
/** The amount of time since the client connected, before time-based checks should become active (to reduce false positives) */
UPROPERTY(config)
int32 InitialConnectToleranceMS;
UE_DEPRECATED(5.1, "This property is no longer supported. Use RPCBlockAllowlist.")
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/Net/RPCDoSDetection.h:1169
Scope (from outer to inner):
file
class class FRPCDoSDetection : protected FRPCDoSState
Source code excerpt:
/** The different RPC DoS detection states, of escalating severity, depending on the amount of RPC spam */
TArray<FRPCDoSStateConfig> DetectionSeverity;
/** The currently active RPC DoS severity state settings */
int8 ActiveState = 0;
/** The worst RPC DoS severity state that has been active */
int8 WorstActiveState = 0;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/Net/RPCDoSDetection.h:1196
Scope: file
Source code excerpt:
double LastPerSecQuotaBegin = 0.0;
/** Stores enough per second quota history, to allow all DetectionSeverity states to recalculate if their CooloffTime is reached */
TArray<FRPCDoSCounters> CounterPerSecHistory;
/** The last written index of CounterPerSecHistory */
int32 LastCounterPerSecHistoryIdx = 0;
/** Counter history which is precalculated/cached from CounterPerSecHistory, for tracking per-period time (up to a maximum of 16s) */
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:67
Scope (from outer to inner):
file
function FDDoSDetection::FDDoSDetection
Source code excerpt:
, bHitFrameNonConnLimit(false)
, bHitFrameNetConnLimit(false)
, DetectionSeverity()
, ActiveState(0)
, WorstActiveState(0)
, LastMetEscalationConditions(0.0)
, bMetEscalationConditionsThisFrame(false)
, bDDoSLogRestrictions(false)
, DDoSLogSpamLimit(0)
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:111
Scope (from outer to inner):
file
function void FDDoSDetection::InitConfig
Source code excerpt:
DDoSLogSpamLimit = DDoSLogSpamLimit > 0 ? DDoSLogSpamLimit : 64;
DetectionSeverity.Empty();
UE_LOG(LogNetCore, Log, TEXT("DDoS detection status: detection enabled: %d analytics enabled: %d"), bDDoSDetection, bDDoSAnalytics);
if (bDDoSDetection)
{
TArray<FString> SeverityCatagories;
int32 HighestCooloffTime = 0;
GConfig->GetArray(DDoSSection, TEXT("DetectionSeverity"), SeverityCatagories, GEngineIni);
for (const FString& CurCategory : SeverityCatagories)
{
FString CurSection = FString(DDoSSection) + TEXT(".") + CurCategory;
if (GConfig->DoesSectionExist(*CurSection, GEngineIni))
{
FDDoSStateConfig& CurState = DetectionSeverity.AddDefaulted_GetRef();
int32 EscalateTime32 = 0;
CurState.SeverityCategory = CurCategory;
GConfig->GetBool(*CurSection, TEXT("bSendEscalateAnalytics"), CurState.bSendEscalateAnalytics, GEngineIni);
GConfig->GetInt(*CurSection, TEXT("EscalateQuotaPacketsPerSec"), CurState.EscalateQuotaPacketsPerSec, GEngineIni);
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:155
Scope (from outer to inner):
file
function void FDDoSDetection::InitConfig
Source code excerpt:
}
if (DetectionSeverity.Num() > 0)
{
DetectionSeverity[ActiveState].ApplyState(*this);
CounterPerSecHistory.SetNum(HighestCooloffTime);
}
else
{
UE_LOG(LogNetCore, Warning, TEXT("DDoS detection enabled, but no DetectionSeverity states specified! Disabling."));
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:177
Scope (from outer to inner):
file
function void FDDoSDetection::UpdateSeverity
Source code excerpt:
void FDDoSDetection::UpdateSeverity(bool bEscalate)
{
int8 NewState = static_cast<int8>(FMath::Clamp(ActiveState + (bEscalate ? 1 : -1), 0, DetectionSeverity.Num()-1));
if (NewState != ActiveState)
{
double CurTime = FPlatformTime::Seconds();
if (bEscalate)
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:195
Scope (from outer to inner):
file
function void FDDoSDetection::UpdateSeverity
Source code excerpt:
while (bCooloffReached && NewState > 0)
{
FDDoSStateConfig& PrevState = DetectionSeverity[NewState-1];
int32 CurStateCooloffTime = DetectionSeverity[NewState].CooloffTime;
check(CounterPerSecHistory.Num() >= CurStateCooloffTime);
for (int32 SecondsDelta=0; SecondsDelta<CurStateCooloffTime; SecondsDelta++)
{
int32 CurIdx = LastCounterPerSecHistoryIdx - SecondsDelta;
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:228
Scope (from outer to inner):
file
function void FDDoSDetection::UpdateSeverity
Source code excerpt:
FDDoSStateConfig& OldState = DetectionSeverity[ActiveState];
FDDoSStateConfig& CurState = DetectionSeverity[NewState];
// If we're at anything other than the base state, then disable all unnecessary logs
bDDoSLogRestrictions = NewState > 0;
ActiveState = NewState;
bMetEscalationConditionsThisFrame = false;
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:291
Scope (from outer to inner):
file
function void FDDoSDetection::PreFrameReceive
Source code excerpt:
}
DetectionSeverity[ActiveState].ApplyAdjustedState(*this, FMath::Max(0.25f, FrameAdjustment));
if (((StartFrameRecvTimestamp - LastPerSecQuotaBegin) - 1.0) > 0.0)
{
UE_CLOG(DroppedPacketCounter > 0, LogNetCore, Warning,
TEXT("DDoS Detection dropped '%i' packets during last second (bHitFrameNonConnLimit: %i, bHitFrameNetConnLimit: %i, ")
TEXT("DetectionSeverity: %s)."),
DroppedPacketCounter, (int32)bHitFrameNonConnLimit, (int32)bHitFrameNetConnLimit,
*DetectionSeverity[ActiveState].SeverityCategory);
// Record the last quota
check(CounterPerSecHistory.Num() > 0);
LastCounterPerSecHistoryIdx++;
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Private/Net/Core/Misc/DDoSDetection.cpp:365
Scope (from outer to inner):
file
function bool FDDoSDetection::CheckNonConnQuotasAndLimits
Source code excerpt:
const int32 PrevState = ActiveState - 1;
if (DetectionSeverity[PrevState].HasHitQuota(*this, TimePassedMS))
{
LastMetEscalationConditions = CurTime;
bMetEscalationConditionsThisFrame = true;
}
}
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Misc/DDoSDetection.h:304
Scope (from outer to inner):
file
class class FDDoSDetection : protected FDDoSPacketCounters, protected FDDoSState
Source code excerpt:
/** The different DDoS detection states, of escalating severity, depending on the strength of the DDoS */
TArray<FDDoSStateConfig> DetectionSeverity;
/** The currently active DDoS severity state settings */
int8 ActiveState;
/** The worst DDoS severity state that has been active - used for limiting analytics events */
int8 WorstActiveState;
#Loc: <Workspace>/Engine/Source/Runtime/Net/Core/Public/Net/Core/Misc/DDoSDetection.h:342
Scope (from outer to inner):
file
class class FDDoSDetection : protected FDDoSPacketCounters, protected FDDoSState
Source code excerpt:
double LastPerSecQuotaBegin;
/** Stores enough per second quota history, to allow all DetectionSeverity states to recalculate if their CooloffTime is reached */
TArray<FDDoSPacketCounters> CounterPerSecHistory;
/** The last written index of CounterPerSecHistory */
int32 LastCounterPerSecHistoryIdx;