Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(state/statebag): filter frequent & large data from client #2362

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <boost/range/adaptors.hpp>
#include <boost/math/constants/constants.hpp>

#include <KeyedRateLimiter.h>

#include <OneSyncVars.h>
#include <DebugAlias.h>

Expand Down Expand Up @@ -4250,8 +4252,81 @@ void ServerGameState::AttachToObject(fx::ServerInstanceBase* instance)
sbac->SetGameInterface(this);

instance->GetComponent<fx::GameServer>()->GetComponent<fx::HandlerMapComponent>()->Add(HashRageString("msgStateBag"),
{ fx::ThreadIdx::Sync, [this](const fx::ClientSharedPtr& client, net::Buffer& buffer)
{ fx::ThreadIdx::Sync, [this, instance](const fx::ClientSharedPtr& client, net::Buffer& buffer)
{
static fx::RateLimiterStore<uint32_t, false> stateBagRateLimiterStore{ instance->GetComponent<console::Context>().GetRef() };

constexpr double kStateBagRateLimit = 75.f;
constexpr double kStateBagRateLimitBurst = 125.f;
static auto stateBagRateLimiter = stateBagRateLimiterStore.GetRateLimiter("stateBag", fx::RateLimiterDefaults{ kStateBagRateLimit, kStateBagRateLimitBurst });

constexpr double kStateBagRateFloodLimit = 150.f;
constexpr double kStateBagRateFloodLimitBurst = 175.f;
static auto stateBagRateFloodLimiter = stateBagRateLimiterStore.GetRateLimiter("stateBagFlood", fx::RateLimiterDefaults{ kStateBagRateFloodLimit, kStateBagRateFloodLimitBurst });

constexpr double kStateBagSizeLimit = 128 * 1024.0;
constexpr double kStateBagSizeLimitBurst = 256 * 1024.0;
static auto stateBagSizeRateLimiter = stateBagRateLimiterStore.GetRateLimiter("stateBagSize", fx::RateLimiterDefaults{ kStateBagSizeLimit, kStateBagSizeLimitBurst });

static fx::KeyedRateLimiter<uint32_t, true> logLimiter { 1.0, 1.0 };

const uint32_t netId = client->GetNetId();

const bool hitRateLimit = !stateBagRateLimiter->Consume(netId);
const bool hitFloodRateLimit = !stateBagRateFloodLimiter->Consume(netId);

if (hitRateLimit)
{
const std::string& clientName = client->GetName();
auto printStateWarning = [&clientName, netId](const std::string& logChannel, const std::string_view logReason, const std::string_view rateLimiter, double rateLimit, double burstRateLimit)
{
console::Printf(logChannel, logReason, clientName, netId);
console::Printf(logChannel, "If you believe this to be a mistake please increase your rateLimiter_%s_rate and rateLimiter_%s_burst. ", rateLimiter, rateLimiter);
console::Printf(logChannel, "You can do this with `set rateLimiter_%s_rate [new value]`. The default rate limit is %0.0f and burst limit is %0.0f\n", rateLimiter, rateLimit, burstRateLimit);
console::Printf(logChannel, "You can disable this warning with `con_addChannelFilter %s drop` if you think you have this properly set up.\n", logChannel);
};

if (hitFloodRateLimit)
{
if (!client->IsDropping())
{
printStateWarning("sbag-client-flood",
"Client %s %d got dropped for sending too many state bag value updates.\n",
"stateBagFlood",
kStateBagRateFloodLimit, kStateBagRateFloodLimitBurst);
}

instance->GetComponent<fx::GameServer>()->DropClient(client, "Reliable state bag packet overflow.");
return;
}

// only log here if we didn't log before, logging should only happen once in every 15 seconds.
if (logLimiter.Consume(netId))
{
printStateWarning("sbag-update-dropped",
"Client %s %d sent too many state bag updates and had their updates dropped.\n",
"stateBag",
kStateBagRateLimit, kStateBagRateLimitBurst);
}

return;
}

uint32_t dataLength = buffer.GetRemainingBytes();
if (!stateBagSizeRateLimiter->Consume(netId, double(dataLength)))
{
if (!client->IsDropping())
{
const std::string& logChannel = "sbag-size-kick";
console::Printf(logChannel, "Client %s %d got dropped for sending too large of a state bag update.\n", client->GetName(), netId);
console::Printf(logChannel, "If you believe this to be a mistake please increase your rateLimiter_stateBagSize_rate. ");
console::Printf(logChannel, "You can do this with `set rateLimiter_stateBagSize_rate [new value]`. The default size rate limit is %0.0f.\n", kStateBagSizeLimit);
console::Printf(logChannel, "You can disable this warning with `con_addChannelFilter %s drop` if you think you have this properly set up.\n", logChannel);
}

instance->GetComponent<fx::GameServer>()->DropClient(client, "Reliable state bag packet overflow.");
return;
}

uint32_t slotId = client->GetSlotId();
if (slotId != -1)
Expand Down
Loading